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
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()
|