MIDI Tools - Tesla Coil MIDI Processing Suite
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

122 lines
4.6 KiB

import mido
import argparse
import sys
import os
def remove_redundant_midi(input_path, verbose=False):
"""
Removes redundant MIDI data from the input MIDI file and saves the cleaned file
with '_redundancy_check' appended to the original filename.
Args:
input_path (str): Path to the input MIDI file.
verbose (bool): If True, prints detailed processing information.
"""
try:
midi = mido.MidiFile(input_path)
if verbose:
print(f"Loaded MIDI file '{input_path}' successfully.")
except IOError:
print(f"Error: Cannot open input MIDI file '{input_path}'. Please check the path.")
sys.exit(1)
except mido.KeySignatureError as e:
print(f"Error reading MIDI file: {e}")
sys.exit(1)
cleaned_midi = mido.MidiFile()
cleaned_midi.ticks_per_beat = midi.ticks_per_beat
total_messages = 0
removed_messages = 0
for i, track in enumerate(midi.tracks):
cleaned_track = mido.MidiTrack()
last_state = {} # To keep track of the last control change or similar messages
last_msg = None
track_removed = 0
track_total = 0
for msg in track:
track_total += 1
total_messages += 1
# Remove consecutive duplicate messages
if last_msg is not None and msg == last_msg:
if verbose:
print(f"Track {i}: Removed duplicate message {msg}")
removed_messages += 1
track_removed += 1
continue
# Remove redundant control change messages
if msg.type in ['control_change', 'program_change', 'pitchwheel', 'aftertouch', 'channel_pressure']:
channel = msg.channel
key = f"{msg.type}_{channel}"
if key in last_state and last_state[key] == msg:
if verbose:
print(f"Track {i}: Removed redundant state message {msg}")
removed_messages += 1
track_removed += 1
continue # Redundant state message
last_state[key] = msg
cleaned_track.append(msg)
# Remove redundant note_off messages if note_on with velocity 0 is used
elif msg.type == 'note_off':
# Depending on MIDI implementation, decide if it's redundant
# For this script, we'll assume they are necessary and keep them
cleaned_track.append(msg)
elif msg.type == 'note_on' and msg.velocity == 0:
# This is equivalent to note_off; decide whether to keep or remove note_off messages
# For simplicity, keep both
cleaned_track.append(msg)
else:
# For all other message types, simply append if not duplicate
cleaned_track.append(msg)
last_msg = msg
cleaned_midi.tracks.append(cleaned_track)
if verbose:
print(f"Track {i}: Processed {track_total} messages, removed {track_removed} redundant messages.")
# Generate the output file name by appending '_redundancy_check' before the file extension
base, ext = os.path.splitext(input_path)
output_path = f"{base}_redundancy_check{ext}"
try:
cleaned_midi.save(output_path)
if verbose:
print(f"Saved cleaned MIDI to '{output_path}'.")
print(f"Total messages processed: {total_messages}")
print(f"Total messages removed: {removed_messages}")
else:
print(f"Successfully saved cleaned MIDI to '{output_path}'.")
except IOError:
print(f"Error: Cannot write to output file '{output_path}'. Please check the path and permissions.")
sys.exit(1)
def parse_arguments():
"""
Parses command-line arguments.
Returns:
argparse.Namespace: The parsed arguments.
"""
parser = argparse.ArgumentParser(description="Remove redundant MIDI data from a MIDI file and save the cleaned file with '_redundancy_check' appended to the filename.")
parser.add_argument('input_midi', type=str, help='Path to the input MIDI file.')
parser.add_argument('-v', '--verbose', action='store_true', help='Enable verbose output.')
return parser.parse_args()
def main():
args = parse_arguments()
# Check if input file exists
if not os.path.isfile(args.input_midi):
print(f"Error: The input file '{args.input_midi}' does not exist.")
sys.exit(1)
remove_redundant_midi(args.input_midi, args.verbose)
if __name__ == "__main__":
main()