I've updated the script to read PM string and it seems to work ok :-
Code: Select all
def mutator_schema(hex_string):
version = int(hex_string[:2], 16)
msb = hex_string[2:4]
lsb = hex_string[4:6]
msb_decimal = int(msb, 16)
lsb_decimal = int(lsb, 16)
total_notes = (msb_decimal - 1) * 256 + lsb_decimal - 1
note_data = []
for i in range(len(hex_string) // 12):
step = int(hex_string[6 + i * 12:8 + i * 12], 16)
pitch = int(hex_string[8 + i * 12:10 + i * 12], 16) - 1
velocity = int(hex_string[10 + i * 12:12 + i * 12], 16) - 1
lengthinsteps = int(hex_string[12 + i * 12:14 + i * 12], 16) - 1
flags = int(hex_string[14 + i * 12:16 + i * 12], 16)
mute = bool(flags & 32)
tied = bool(flags & 16)
octave_offset = flags & 7 - 3
note_data.append((step, pitch, velocity, lengthinsteps, mute, tied, octave_offset))
return version, total_notes, note_data
# sample data
#pattern_data_1 = "020110013D67028380023D5E028380033D63028380043D35028380053D6A028380063D54028380073D36028380083D69028380093D300283800A3D2E0283800B3D610283800C3D320283800D3D670283800E3D340283800F3D43028380"
pattern_data_1 = "020108013D65118380113D65098380193D650583801D3D650383801F3D65028380203D65028380213D65028380"
version, total_notes, note_data = mutator_schema(pattern_data_1)
print(f'{version} {total_notes}')
for note in note_data:
step, pitch, velocity, lengthinsteps, mute, tied, octave_offset = note
print(f"Note: pos={step:02}, pitch={pitch}, velocity={velocity:03}, length={lengthinsteps}, mute={mute}, tied={tied}, octave_offset={octave_offset}")
Code: Select all
import sys
from mido import MidiFile
def warningnotice():
try:
print("This script is almost certainty broken and should only be used if you accept responsibility")
agree = input("Do you argee (y/n): ")
if agree.lower() == "n":
print("Exiting the program...")
sys.exit(0)
except Exception as e:
print(f"An error occurred: {e}")
sys.exit(1)
ppqn = 0
whole_note_duration = 0
notescalefactor = 254
version = '02'
note_count = 0
max_notes = 340
flags = '8380' # dont think i need to worry about this, so long as its added.
offset = 1
pattern_data = []
pattern_data_1 = ''
def calculate_standard_notation(note_duration):
# Calculate the length of the note in standard notation
standard_notation = ""
if note_duration == whole_note_duration:
standard_notation = "Whole Note"
elif note_duration == whole_note_duration / 2:
standard_notation = "1/2 Note"
elif note_duration == whole_note_duration / 4:
standard_notation = "1/4 Note"
elif note_duration == whole_note_duration / 8:
standard_notation = "1/8 Note"
elif note_duration == whole_note_duration / 16:
standard_notation = "1/16 Note"
elif note_duration == whole_note_duration / 32:
standard_notation = "1/32 Note"
elif note_duration == whole_note_duration / 64:
standard_notation = "1/64 Note"
elif note_duration == whole_note_duration / 128:
standard_notation = "1/128 Note"
return standard_notation
def calculate_totalnotes_value(integer):
msb = (integer >> 8) & 0xFF
lsb = integer & 0xFF
msb += offset
lsb += offset
total_notes_hex = f"{msb:02X}{lsb:02X}"
return total_notes_hex
def read_midi_file(file_path):
mid = MidiFile(file_path)
ppqn = mid.ticks_per_beat
global whole_note_duration
whole_note_duration = ppqn * 4
global note_count
pos = 1
note_length = 1
for i, track in enumerate(mid.tracks):
if track.name > '':
print(f'Track [{i:03}] Name: {track.name}')
for j, msg in enumerate(track):
if note_count == max_notes:
print('max note count reached')
break
if msg.type == "note_on":
note_on_time = msg.time
note_off_time = track[j + 1].time
note_duration = note_off_time - note_on_time
standard_notation = calculate_standard_notation(note_duration)
if standard_notation >"":
note_count += 1
if standard_notation == "Whole Note":
note_length = 16
elif standard_notation == "1/2 Note":
note_length = 8
elif standard_notation == "1/4 Note":
note_length = 4
elif standard_notation == "1/8 Note":
note_length = 2
elif standard_notation == "1/16 Note":
note_length = 1
elif standard_notation == "1/32 Note":
note_length = 1
elif standard_notation == "1/64 Note":
note_length = 1
elif standard_notation == "1/128 Note":
note_length = 1
# still note sure about scale factor and what value it is used for?
print(f'Note [{note_count:03}] ON Pos [{pos:03}] Length [{note_length:02}] Factor {int(note_duration / notescalefactor):02X}')
pattern_data.append((pos, msg.note, msg.velocity, note_length, flags))
pos += note_length
# this is now adding an extra patterdata note ? i think i broke the logic......
if msg.type == 'note_off':
print(f'Note [{note_count:03}] OFF Pos [{pos:03}] ')
global pattern_data_1
steps = calculate_totalnotes_value(note_count)
pattern_data_1 = f'{version}{steps}'
print(f'PM Version {version} Note Count {steps}')
for k, data in enumerate(pattern_data):
pos, note, velocity, length, flag = data
pattern_data_1 += f'{pos:02X}{note+offset:02X}{velocity+offset:02X}{length+offset:02X}{flag}'
print(f'[{k+1:02}] {pos:02X} {note+offset:02X} {velocity+offset:02X} {length+offset:02X} {flag}')
print(f'\n{pattern_data_1}')
if len(sys.argv) > 1:
# i'm still working on this, so there is defo bugs....
warningnotice()
read_midi_file(sys.argv[1])
else:
print("Please provide a midi filename.")