#!/usr/bin/env python3 # binary_to_midi.py # # Author: Intel A80486DX2-66 # License: Creative Commons Zero 1.0 Universal or Unlicense # SPDX-License-Identifier: CC0-1.0 OR Unlicense from decimal import Decimal from midiutil import MIDIFile from os import stat from sys import argv NOTE_TABLE = {i: 52 + i for i in range(8 * 2)} is_prime = lambda n: n > 1 and all(n % i for i in range(2, n)) TEMPO = 110 TIME_SIGNATURE = (4, 4) INSTRUMENT_SELECTED = 1 STEP_SIZE = 1 DURATION_CONSTANT = 32 def binary_to_midi(input_file, output_file): print("Reading file") with open(input_file, "rb") as f: byte_data = f.read() print("Initialization") pattern = MIDIFile(1, TIME_SIGNATURE, TEMPO) pattern.addTempo(0, 0, TEMPO) track = 0 channel = 0 time = 0 chords = [] current_chord = [] previous_note = NOTE_TABLE[0] byte_step = (len(byte_data) // 2**6) + 1 print("Converting") taking_chord = False for i, byte in enumerate(byte_data[::STEP_SIZE]): is_rest = False note = NOTE_TABLE[byte % len(NOTE_TABLE)] is_previous_note = i > 0 and note == previous_note if byte % 51 == 25: is_rest = True elif byte % 12 == 0: current_chord = [note] chords.append(current_chord) taking_chord = True elif taking_chord: current_chord.append(note) taking_chord = False duration = (Decimal('0.25') if is_prime(byte) or byte % 11 < 6 else \ Decimal('0.015')) * DURATION_CONSTANT if abs(previous_note - note) > 3 and not is_previous_note: current_chord.append(0) previous_note = note pattern.addProgramChange(0, 0, 0, INSTRUMENT_SELECTED) for chord in chords: for note in chord: if is_rest: pattern.addNote(track, channel, 0, time, duration, 0) else: pattern.addNote(track, channel, note, time, duration, 100) time += duration print("Writing out") with open(output_file, "wb") as file: pattern.writeFile(file) print(f"Saved, {stat(output_file).st_size} bytes") if __name__ == "__main__": if len(argv) < 2: print("Fatal error: No file specified as argument.") exit(1) binary_to_midi(argv[1], "output.mid")