MiniDexed TX816 – Part 6 – Pico MIDI Router and TX816 IO Code

It’s been a while since finishing the MiniDexed TX816 Mechanical Assembly and I had a bit of a distraction recently rebuilding my Lo-Fi Orchestra, but I’ve also been tinkering with the code for the TX816 IO panel and router.  This post looks into a bit more detail what it is all doing.

Posts in this series:

  • Part 1: Introduction, context, and high-level design.
  • Part 2: PCB design.
  • Part 3: Panel design.
  • Part 4: PCB assembly.
  • Part 5: Mechanical assembly.
  • Part 6: Pico MIDI Router and TX816 IO Code.
  • Part 7: In use!

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

If you are new to microcontrollers and single board computers, see the Getting Started pages.

Core Functional Modules

Many of the required elements have been met already as part of the test code, but to recap, these are the main modules to be implemented:

  • MIDI routing, taking MIDI IN on channels 1-16 and mapping them onto tone generators using MIDI OUT but this time on channels 2-9 with “common” mode on channel 1 (see later).
  • MIDI message parsing, both for the routing but also for any communications between the Pico and the Raspberry Pi running MiniDexed.
  • MCP3008 code to handle the reading of the potentiometers.
  • HC595 shift register code to handle the LEDs.
  • MAX7129 code to handle the seven segment displays.
  • Switch handling code to handle, well, the switches!

And all this needs to be scheduled so as not to introduce any latency in the MIDI handling.

So, on that last point, the core principle I’m following is: handle MIDI on every “scan” through the main loop, but only handle the smallest amount of IO I think I can get away with.

Consequently I’ve adopted the following structure for the main sequence of events:

while True:
  Handle MIDI routing

  Alternate:
    Scan switches and LEDs
  Or:
    Scan pots and 7-segment display

And in the sub-functions for the switches and pots, I’ve taken an approach similar to the following:

scanSwitches():
  IF still scanning switches THEN
    Scan each switch in turn, one per call of this function
    return
  IF all switches have been scanned THEN
    Prepare the value for all LEDs to be written out to the 595
    return
  IF value ready to output THEN
    Output the value to the 595
    return

The scanPots() function does the same for the potentiometers, then preparing the display string for the 7-segment display and then actually outputting it when ready.

This means that the slowest operations per scan will be the two that involve streaming the LED values out to the 595 or writing out to the MAX7219s – both of which have to happen in a single sequence really.

But each function will return between ever other operation allowing MIDI handling to keep functioning as fast as possible.

This is all currently implemented in Micropython and the performance seems ok.  Two other avenues to explore to increase the responsiveness will be:

  • Use multi-threading in Micropython to place all the MIDI routing on the second core of the Pico.
  • Re-write the entire application using the C/C++ SDK (and maybe also use the second core).

I tried using the second core, which was going to be my preferred route, but unfortunately it was pretty problematic. That is something I’d like to come back to if the Micropython multi-core support can settled down a bit more.

Update: A short note on various indexes used in the code.

I’ve clarified the functionality of the code to make it a little clearer when the code is using a tone generator number, a MIDI channel or an index for a pot/switch or LED.

The main indexes now used in the code are as follows:

  • Tone Generators (TG): 1-8
  • MIDI IN channels: 1-16
  • MIDI OUT channels: 2-9 (mapped onto TGs 1-8)
  • Index for potentiometers, LEDs, switches, 7-segment display: 0-7 (corresponding to TGs 1-8)

The default MIDI IN channel for “common” mode is channel 1 and the default MIDI IN channel mapping is channels 2-9 for TGs 1-8 – so the same as the MIDI OUT channel mapping.  But all MIDI IN channels are fully configurable via the MIDIRT[] array.

Note that the MIDITG[] array, which defines which MIDI channels to use on the OUT side to MiniDexed, has to match the performance.ini settings in MiniDexed itself.

Basic IO Code

The main IO code is largely as used in the test code, using libraries for the MCP3008 and MAX7219 with a few minor tweaks.  The HC595 code is my own (simple) implementation of the required sequence of IO handling.

The switch handling is simply scanning the relevant IO pins and turning those into LED patterns again has all been described previously.

Individual/Common Mode

A key feature I wanted to keep from the original TX816 is the idea of being able to enable a “common” mode whereby all tone generators can be played at once.  A single press of the button will switch that specific tone generator between the two modes.

Update: In the original code a short press toggled between individual and common mode, but after some initial trials, it was changed to a long press.

To implement this, I have two functions now for sending MIDI note messages:

  • midiSendToCommon – will send the provided MIDI note messages to all tone generators in common mode.
  • midiSendToInd – will send the provided MIDI note messages to the required tone generator in individual mode, as determined by the MIDI routing table.

There is a structure with a True/False indication for each tone generator to decide which mode it is in and there is a definition of the MIDI channel to be used as the “common” channel.  This can be one of the existing tone generators (in which case, the TG on that channel already is effectively always in common mode) or one of the unused channels 10-16.

The logic being followed in the code is essentially as follows:

HandleNoteOn:
  IF note is sent to the COMMON channel THEN
    Send note to all TGs that are in COMMON mode
    IF no TGs are in COMMON mode, THEN treat as individual mode
  ELSE
    Work out which TG to use for this channel from the routing table
    Send note just to this TG

User Interface Functionality

The main use of the pots and 7-segment displays is for the voice selection, but I wanted to allow for other parameters too, so have opted to also use the button to change the “uimode”.  The following modes are supported:

  • Normal: UI controls the voice for the TG (max of 32 voices).
  • Volume: UI controls the volume for the TG (max of 64 steps).
  • Bank Select: UI controls the bank selection for the TG (max of 8 banks).
  • Detune: UI supports a +/- detuning of the TG (up/down 15 points).

Update: The original code used a “long press” to change the uimode and a “short press” to swap between individual and common operation, but after some initial use, I updated the code to swap these actions.  Now, changing UI mode is done with a short press and press and holding will swap between individual and common mode.

The “fun” part is deciding how to use the 7-segment displays and LEDs to indicate the mode and current parameters!

Here is the main “key”:

  • LED will flash when in one of the additional (non-normal) modes.
  • 7-segment decimal points will be used to indicate which of the additional modes is selected:
    • No decimal points: changing volume – display shows value from 1 to 64.
    • Second (right) decimal point: changing bank – display shows “b” followed by the bank number from 1 to 8.
    • First (left) decimal point: changing detune – displays shows level of negative detune (left-hand digit – 0 to F) or positive detune (right hand digit – 0 to F).  Showing 00 means no detune.

There are two rough edges to using potentiometers and MIDI as the control method for the above:

  • The pot has an absolute position, unlike a rotary encoder which is relative.  Consequently, when switching modes, the display will reflect the previously chosen value for the parameter, not the current pot position.  Moving the pot will update the display and value.
  • It is possible to change parameters on MiniDexed itself using the rotary encoder and OLED display but these changes will not be reflected back to the IO panel and the 7-segment displays.

I have a few ideas for the second point, but it basically requires a MIDI “back channel” from MiniDexed to the Pico, which will probably require a custom MiniDexed build.  I might come back to this one.

Specific Additional Code Functionality

I’m not going to go into full detail about how all the code is working, but I’m just adding a couple of extra notes here about a few specific items.

LED flashing: The functions that decide which LEDs are on and off also has to take into account the fact that the LEDs will flash in response to MIDI activity, and that they will have a regular flash when in one of the additional UI modes.  This is all taken care of by awareness of uimode and a set of lists of additional LED “mask” values that will be ANDed with the actual value at the right time.

Switch long pressing: The switch handling has to recognise HIGH to LOW events (switch has been pressed); LOW to LOW events (switch is still being pressed); and LOW to HIGH events (switch has been released).  In the case of LOW to LOW if the “long press timeout” occurs, then the code considers the switched “long pressed” and acts according. In the case of LOW to HIGH events, if the “short press” timeout was not reached, then it is considered a short press. If the “long press” has already happened, then nothing else happens here.

MIDI Interface to MiniDexed: The follow MIDI messages can be injected into the MIDI stream in response to UI events:

  • MIDI PC – to select a voice.
  • MIDI CC 7 – to change volume.
  • MIDI CC 32 – to select a voice bank (uses the LSB only).
  • MIDI CC 94 – to set the detuning parameter (“MIDI Effect 4 Depth).

MIDI SysEx handling: I toyed with the idea of processing MIDI SysEx voice dumps as a way of working out which voice was selected via the MiniDexed UI – as it has the option of sending a voice dump on Program Change.  Consequently I have some simple SysEx handling in place, but this has gone no further at present.  The voice number isn’t present in the voice dump – I’d have to do something like store the checksums of known voices to work out which voice has been dumped and thought that was probably too much work right now.  But there is code in there to listen for MIDI returning via the IN on uart 1 for potential processing in the future.

MiniDexed Configuration

The performance.ini configuration is now set so that TG 1 is listening on MIDI channel 2; TG 2 on MIDI channel 3; and so on.  This means that the interface between the Pico and MiniDexed uses MIDI channels 2 to 9 for Tone Generators 1 to 8.

Externally, the MIDI channel configuration, by default, is as follows:

  • Channel 1 – the TX816 “common” channel.
  • Channels 2-9 – the TX816 TG 1-8 “individual” channels.
  • Channels 10-16 – not used.

I have a specific version of this “external” configuration that I can use with my Lo-Fi Orchestra.

Conclusion

The Pico code as it stands is available on Github here: https://github.com/diyelectromusic/sdemp/tree/main/src/SDEMP/MiniDexedTX816

WARNING: At best, this has to be considered as “work in progress” and “use at your own risk” and so on.

The video above shows the following in sequence (note that the MIDI COMMON channel is set to 1):

  • Changing the voice on TG 2.
  • Changing voices on TG 4 and 3.
  • Setting TG 2, 3 and 4 to COMMON mode.
  • Turning the volume down on TG 1.
  • Detuning TG 3 and 4.
  • Changing voices on TG 5 and 6 then setting to COMMON mode.
  • Setting all TGs back to INDIVIDUAL mode.
  • Playing each TG individually.

It’s a pretty basic video but hopefully it shows how the main controls work.  Note that the video is of the original “short press to set common mode” version.

Things I’m still thinking about:

  • I’m wondering about swapping the long/short press actions on the buttons – so make short press change the UI mode and make long press swap between IND/COMMON.
    • Update: After initial use, yes – I swapped them over!  Changing parameters is so much easier with a short press.
  • I’m wondering about intercepting PC and CC messages and use them to update the internal status of the IO board.  This could happen in both directions – so from the outside from an external controller, but also from the MiniDexed itself.

The performance is not too bad, but there are still plenty of examples of it struggling – especially when I start throwing some of my Lo-Fi Orchestra arrangements at it…

Talking of which… onto part 7…

Kevin

Leave a comment