MicroPython MIDI mayhem (kinda)

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:

Raspberry Pi Pico with small piezo speaker connected to pins 23 and 26
piezo between pins 26 and 23

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:

apologies to LG

It works! It works!

on a messy desk, a small USB midi keyboard is connected to a Korg NTS-1 mini synthesizer via a small micro-controller board that acts as a USB host for the Akai keyboard, converting USB MIDI to traditional MIDI for the Korg
Akai LPK25 keyboard has USB MIDI out, but the Korg NTS-1 only has regular MIDI in. The little board in the middle acts as a USB host for the Akai and MIDI source for the Korg

This is great: gdsports/midiuartusbh: MIDI DIN to MIDI USB Host Converter allows your USB MIDI instruments to act as traditional MIDI controllers. It uses a Adafruit Trinket M0 to act as the USB host and MIDI output.

I modified gdsports’ design very slightly:

  1. Instead of using a 74AHCT125 Logic level converter and driver, I used a FET-based SparkFun Logic Level Converter
  2. Instead of a 5-pin DIN socket, I used a 3.5 mm stereo socket.

And it works!

breadboard showing Trinket M0 microcontroller board, logic level shifter, audio socket breakout and two resistors
Breadboard layout for MIDI-standard 3.5 mm output (Korg). The resistors are both 220 ohm, and the boards need 5 V power