Since 2023, the development build of DefleMask (available on their Patreon) features an AI-assisted channel router.
Settings to use:
Click "Import." The new algorithm will analyze the MIDI's polyphony and automatically split chords across multiple FM channels. midi to dmf new
Converting MIDI to DMF New requires three distinct stages: Temporal Quantization, Semantic Mapping, and Event Synthesis.
Parse and merge MIDI events:
function parse_midi(file):
header = read_header(file)
ppq = header.ppq
tracks = [parse_track(t) for t in file.tracks]
events = merge_tracks_by_delta_time(tracks)
return events, ppq
Build absolute times:
function build_timing(events, ppq):
tempo_map = [(0, 500000)] // default microseconds per quarter
absolute_time = 0
for ev in events:
absolute_time += (ev.delta_ticks / ppq) * current_tempo_us_per_qn
if ev.type == TEMPO:
current_tempo_us_per_qn = ev.tempo
tempo_map.append((absolute_time, current_tempo_us_per_qn))
ev.time_ms = absolute_time / 1000
return events, tempo_map
Event to DMF mapping (simplified):
for ev in events:
if ev.type == NOTE_ON and ev.velocity > 0:
dmf_event = DMFNote(start=ev.time_ms, duration=calc_duration(ev), pitch=ev.note, vel=ev.velocity, channel=ev.channel)
elif ev.type == NOTE_OFF or (ev.type==NOTE_ON and ev.velocity==0):
// handled by matching note's duration earlier
elif ev.type == PROGRAM_CHANGE:
dmf_event = DMFProgramChange(time=ev.time_ms, channel=ev.channel, program=map_gm_to_dmf(ev.program))
elif ev.type == CONTROL_CHANGE:
dmf_event = DMFController(time=ev.time_ms, controller=ev.controller, value=ev.value)
...
append_to_segment(dmf_event)
Complexity: O(N log T) where N events, T active notes for lookups; with hash maps reduces to O(N).
After import, you have a .dmf file. You must do three things manually because no automated tool is perfect: Since 2023, the development build of DefleMask (available