Raspberry Pi Pico MIDI Note Balancer

One use for my Raspberry Pi Pico MIDI Splitter is as a way to get pseudo polyphony from a range of monophonic sources.  I’ve called this “note balancing” from the idea of “load balancing” online systems – sharing out the “ask” across a number of devices.  In a similar way, this can take requests to play a note and “balance” them out across a set of different devices.

I’ve used my Raspberry Pi Pico MIDI Splitter (TTL) to drive my Arduino Nano Multi-tone or PWM PCB by way of an example, and the code has been written for my Splitter PCBs, but it is possible to go back to a solderless breadboard version as described in my original project here: Raspberry Pi Pico MIDI Channel Router.

IMG_6929

Warning! I strongly recommend using old or second hand equipment for your experiments.  I am not responsible for any damage to expensive instruments!

These are the key Arduino tutorials for the main concepts used in this project:

If you are new to microcontrollers, see the Getting Started pages.

Parts list

The Circuit

IMG_6930

In this project, the splitter’s OUTPUTS 1-4 are connected to the tone PCB’s INPUTS D-A and ground is linked up.  Both boards are powered independently.  Recall that the tone PCB requires a centre-NEGATIVE barrel jack connector; the Pico is powered using the micro-USB link.

Also note the comments on “parasitic power” if the Arduino is powered up before the Pico.

The jumper settings for the tone PCB are such that each Nano has an independent INPUT.  There is no “chaining” going on in this configuration (see photo).

The Code

The code is an evolution of the Raspberry Pi Pico MIDI Channel Router.  The router has a fixed configuration – it has 8 OUTPUT channels via the Raspberry Pi PIO and they are mapped onto consecutive MIDI channels.

For the note balancer, the mapping between MIDI channels and OUTPUT MIDI ports needs to be configurable in a way that allows a single MIDI channel to span multiple OUTPUT ports.  This is in contrast to my Raspberry Pi Pico Multi MIDI Router which has a range of flexible INPUT and OUTPUT mappings.

The note balancer works from the following core routing structure:

MIDIRT:
   For each MIDI channel to be routed:
      List the OUTPUT ports and the number of notes they support

The structure is defined using python lists and dictionaries.  Here is an example that supports 3 MIDI channels: channel 1 for six notes across three ports; channel 2 for 4 notes on one port; channel 3 for two notes across two ports:

MIDIRT = [
  {'ch':1, 'ports':[
    # Three ports, each with 2-note polyphony
    {'port':0, 'playing':[0,0]},
    {'port':1, 'playing':[0,0]},
    {'port':2, 'playing':[0,0]}
    ]
  },
  {'ch':2, 'ports':[
    # One port, with 4-note polyphony
    {'port':3, 'playing':[0,0,0,0]}
    ]
  },
  {'ch':3, 'ports':[
    # Two ports, with single note polyphony
    {'port':4, 'playing':[0]},
    {'port':5, 'playing':[0]}
    ]
  }
  ]

The code supports the concept of limited polyphony on any OUTPUT port.  In the above structure, the ‘playing’ key contains a lists of “last playing” notes.  When these are set to zero, the “slot” is free to accept a note.  When all slots on one port are playing notes, the next free slot on the next port for the channel is used.

Allocation of slots happens on reception of NoteOn messages, and slots are freed on reception of NoteOff messages.  A function, “midi2port” handles all this and returns the port to which the message has to be forwarded on to.  Only NoteOn and NoteOff messages are processed.  All other messages are dropped, so the note balancer will also act as a MIDI filter too and filter out any non-note messages.

Here is the logic to find the next free slot for a NoteOn message:

if cmd == 0x90:
  for r in MIDIRT:
    if r['ch'] == ch:
      for p in r['ports']:
        for n in range(len(p['playing'])):
          if p['playing'][n] == 0:
            # There are free playing slots on this port
            p['playing'][n] = note
            # Only return first free port
            return p['port']

The NoteOff logic is pretty much the same apart from the final test that looks for p[‘playing’][n] == note instead of zero.

Also, if messages are received on channels with no entry in the routing table, they too are dropped.

The code includes a periodic printout of the MIDIRT every so often so that it is easy to see if everything is working correctly.  If that is not required (and it shouldn’t be once a routing setup is determined to work ok) then it can just be commented out in the main loop.

There are a number of configuration settings at the top of the code too:

UART_BAUD = 31250
HW_UART = 0
PIN_BASE = 6
NUM_UARTS = 8

MIDI_THRU = True
MIDI_LED = 25
NUM_PORTS = 6

The top four are pretty much fixed for the splitter PCB and setup.  The last three are options.

MIDI_THRU enables a software THRU mode in addition to the PIO routing, which will echo every received byte from the hardware MIDI IN back out to the hardware MIDI OUT.  The routing only affects the PIO MIDI ports.

MIDI_LED optionally provides a crude MIDI indicator LED.  25 is the built-in LED for the Pico.

NUM_PORTS limits the number of available ports if only sub-set of the 8 possible ports are used.

Find it on GitHub here.

Closing Thoughts

The routing structure seems to work fairly well.  This was about the third iteration – I started with ports, then per channel, then an indicator of polyphony, until finally realising that polyphony was best expressed literally as the number of “slots” in the structure for notes.  I hope that makes sense in the future.

This might seem like a bit of a niche application, but the main use-case for me is for my Lo-Fi Orchestra rebuild.

Kevin

One thought on “Raspberry Pi Pico MIDI Note Balancer

Leave a comment