It pleased me to learn about umidiparser – MIDI file parser for Micropython. Could I use my previous adventures in beepy nonsense to turn a simple MIDI file into a terrible squeaky rendition of same? You betcha!
MIDI seems to be absurdly complex. In all the files I looked at, there didn’t seem to be much of a standard in encoding whether the note duration was in the NOTE_ON event or the NOTE_OFF event. Eventually, I managed to fudge a tiny single channel file that had acceptable note durations in the NOTE_OFF events. Here is the file:
I used the same setup as before:
With this code:
# extremely crude MicroPython MIDI demo
# MicroPython / Raspberry Pi Pico - scruss, 2022-08
# see https://github.com/bixb922/umidiparser
import umidiparser
from time import sleep_us
from machine import Pin, PWM
# pin 26 - GP20; just the right distance from GND at pin 23
# to use one of those PC beepers with the 4-pin headers
pwm = PWM(Pin(20))
led = Pin('LED', Pin.OUT)
def play_tone(freq, usec):
# play RTTL/midi notes, also flash onboard LED
# original idea thanks to
# https://github.com/dhylands/upy-rtttl
print('freq = {:6.1f} usec = {:6.1f}'.format(freq, usec))
if freq > 0:
pwm.freq(int(freq)) # Set frequency
pwm.duty_u16(32767) # 50% duty cycle
led.on()
sleep_us(int(0.9 * usec)) # Play for a number of usec
pwm.duty_u16(0) # Stop playing for gap between notes
led.off()
sleep_us(int(0.1 * usec)) # Pause for a number of usec
# map MIDI notes (0-127) to frequencies. Note 69 is 440 Hz ('A4')
freqs = [440 * 2**((float(x) - 69) / 12) for x in range(128)]
for event in umidiparser.MidiFile("lg2.mid", reuse_event_object=True):
if event.status == umidiparser.NOTE_OFF and event.channel == 0:
play_tone(freqs[event.note], event.delta_us)
This isn’t be any means a general MIDI parser, but is rather specialized to play monophonic tunes on channel 0. The result is gloriously awful: