Arduino MT-32 Program Changer

One thing that I always found slightly annoying with my MT-32 Synth Module, and something I thought would be a nice addition for my MT-32 Pi, was a two-knob voice selector.  This project uses an Arduino Nano to create one to send Program Change messages over MIDI to an MT-32.

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 Arduino, see the Getting Started pages.

Parts list

  • Arduino Nano or Uno
  • 0.96″ 128×64 OLED I2C Display
  • 2x 10K potentiometers
  • MIDI Out interface (see: Arduino MIDI Interfaces)
  • Breadboard and jumper wires

The Circuit

ArduinoMT32PatchChanger_bb

The MT-32 has a user interface that allows you to select either the group or an individual voice within the group, but you have to choose which one and then use a single knob to select it.  I wanted to replicate that with two potentiometers and an Arduino, but thought it would be nice to add a display too.

This is using one of the Arduino MIDI Interfaces for MIDI out, connected to the Arduino’s 5V, GND and TX pins.  I’m using an Arduino Nano, so for me this is the first pin near the programming header.  If you are using an Uno, it will be the second pin in.

The setup is powered via the USB link on the Arduino.

2021-04-07 11.15.32

The Code

The MIDI side, program change messages and use of the OLED display has been covered elsewhere (see here and here) so I won’t go over that again here.  Note that you have to specify the I2C address of the OLED display.  The modules I’m using are typically either 0x3C or 0x3D.  Set this using the OLED_ADDR definition near the top of the code.

One of the challenges of this project is that the MT-32 doesn’t have a simple relationship between groups and voices!  Some of the groups contain eight voices, some as few as four, and some as many as ten or eleven.  So to have the code be able to choose the group and voice within the group separately is a bit complicated.

I defined three lists to help:

  • mt32groups – contains the textual names of all 17 groups.
  • mt32voices – contains the textual names of all 128 voices.
  • mt32VinG – contains the numbers of voices in each group.
uint8_t mt32VinG[MT32GROUPS]={
   8,8,8,4,4,8,8,11,5,8,6,10,9,8,7,10,6
};

As the Arduino has limited dynamic memory, the strings all have to be defined as const PROGMEM arrays.

const char PROGMEM mt32groups[MT32GROUPS][MT32SIZE]={
"Piano",
"Organ",
...
"Effects"
};

But pulling strings out of PROGMEM isn’t as simple as handling normal strings, and I’m looking to display them on an OLED display, which has its own “print” function, thanks to the Adafruit libraries.  In the end I pull the strings out character by character and pass them over to the display print function as illustrated below.

void printGroup (int group) {
  for (int i=0; i<MT32SIZE; i++) {
    char c = pgm_read_byte_near(&mt32groups[group][i]);
    if (c==0) break;
    display.print(c);
  }
  display.println();
}

This relies on two things: I know the maximum size of each string (MT32SIZE) as this is used to create the array in the first place; strings are terminated in C/C++ with a “NULL” character, i.e. a zero.  So when the code reads a zero out of memory I break out of the loop and stop printing characters.

The potentiometers give me a reading between 0 and 1023 so these need to be scaled to fit the number of groups and maximum number of voices per group.

int pot1 = analogRead(POT_G) / (1024/MT32GROUPS);
int pot2 = analogRead(POT_S) / (1024/MT32MAXVING);

But I also need to know the “voice number” both to send in the MIDI Program Change command and also to find the right voice string in the complete list of 128 voices.  However as the number of voices in each group is different, I see no other way than to work through the mt32VinG list and add up the totals until I get to the group and voice I’m interested in. I hide that away in a helper function though.

int calcMT32Voice () {
  int voice = 0;
  for (int i=0; i<pot_group; i++) {
    voice += mt32VinG[i];
  }
  voice += pot_sound;
  return voice;
}

pot_group and pot_sound are the scaled values read from the potentiometers and used to select the group and voice within the group respectively.

The only other thing to worry about is not sending program change messages too often, so I only do it if the pot values have changed.  As part of this, I also need to check that the sound pot can’t select beyond the last voice in a group.  This all happens when reading the pots.

if (pot1 >= MT32GROUPS) pot1 = MT32GROUPS-1;
if (pot2 >= mt32VinG[pot1]) pot2 = mt32VinG[pot1]-1;

if ((pot_group != pot1) || (pot_sound != pot2)) {
  pot_group = pot1;
  pot_sound = pot2;
  mt32voice = calcMT32Voice();
  updateDisplay(pot_group, pot_sound);
  mt32ProgramChange(mt32voice);
}

I think is about it.  I suspect a General MIDI equivalent would be quite a lot easier as the groups tend to have the same number of voices in them, unlike the MT-32!

Find it on GitHub here.

Closing Thoughts

One reason for doing this is to start thinking about some kind of user interface for my MT-32 Pi, but unfortunately right now it can’t accept MIDI over USB and the 5-pin serial line at the same time (it might come along in the future), so to really be useful I’ll need some kind of “MIDI merge” functionality at some point.

One option would be to include a MIDI in option for this project and re-route anything received over MIDI back out again.  In fact, I believe the default MIDI library action is to enable this kind of “software THRU” anyway.

But what I’d really like is a USB version with USB MIDI receiving from a MIDI device (so acting as a USB MIDI host) and then sending to the MT-32 Pi (so acting as a USB MIDI device too).  But that is a little complicated to think about right now.

Kevin

2021-04-07 11.15.43

Leave a comment