Arduino MIDI Filter

I am working on something that might allow several of my projects to come together, but one issue I’m having is the overheads of processing unnecessary MIDI traffic are causing delays in playing for some of the modules.  So I thought I’d create a MIDI filter that can be used to see all the traffic and filter out any not on a specific channel – sort of like a “MIDI co-processor”.

It turns out this is relatively simple to do.

Warning! I strongly recommend using an old or second hand keyboard for your MIDI 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 Uno or Nano
  • One of the Arduino MIDI Interfaces
  • Source of MIDI data and something to send it to
  • Breadboard and jumper wires

The Circuit

This utilises the RX and TX pins using, as you might expect, RX to receive the full, unfiltered MIDI message stream, and TX to send out the filtered stream.

The Code

The basic process is as follows:

Look for MIDI messages and when found:
Check if it is on a channel we are interested in, IF NOT ignore
Check if it is a MIDI command we are interested in, IF NOT ignore
ELSE send the MIDI message

There are several ways that the choice of channels can be specified.  I opted for a “bitmask” as there are 16 MIDI channels, I used a uint16_t type variable – one bit for each channel. If a bit is set to 1, then the corresponding MIDI channel is passed through the filter.

The only thing to watch out for is that the MIDI library may indicate that there is no specific channel associated with a command – this is the case with system exclusive messages for example, so I have to watch out for that.

Also the MIDI library (and MIDI channels) are numbered 1 to 16, but to use as a bitmask I need values 0 to 15.

The code to check the filter looks like this – this will pass through MIDI data on channel 1:

// Use binary to say which MIDI channels this should respond to.
// Every "1" here enables that channel. Set all bits for all channels.
//
// 16 12 8 4 1
// | | | | |
uint16_t MIDI_CHANNEL_FILTER = 0b0000000000000010;

// rest of code in setup()

void loop () {
// rest of code in loop()
byte ch = MIDI.getChannel();
uint16_t ch_filter = 1<<(ch-1);
if (ch == 0) ch_filter = 0xffff;
if (MIDI_CHANNEL_FILTER & ch_filter) {
// Now we've filtered on channel, filter on command
byte cmd = MIDI.getType();
switch (cmd) {
}
}
}

Once I know the message is on a wanted channel I can look at the MIDI command and further filter on that.  In my case I only want to pass on MIDI Note On and Note Off messages.

Putting one of these filters on front of my other synth modules has significantly reduced the overheads of having to process and ignore unwanted MIDI data.

Find it on GitHub here.

Update: This is an update to this code which adds optional “send a program change message” functionality here: Arduino MIDI Filter – Revisited.

Closing Thoughts

This is the kind of processing that would be really useful to be able to embed in a MIDI shield itself – sort of like a “MIDI co-processor” but the shield would need some processing power first.  It would also be nice to be able to select the MIDI channels to pass through using switches or jumpers.

Maybe a simple Uno shield could support an Arduino Nano “co-processor”.  But that might also be a little over the top.  There are other smaller microcontrollers that might be able to do the job instead.

Kevin

 

6 thoughts on “Arduino MIDI Filter

  1. Thank you! i will build this as soon as i get home! Was hoping to find such a midifilter design for my quasimidi sirius synthesizer, which accepts and sends different audio for 6 midichannels…and i only need 1. This cheap fix suits my needs and probably other owners of the quasimidi sirius

    Like

  2. Thank you! I find your web page/projects informative, complete, and easy for a (somewhat) beginner. I have adapted this to make a filter for my Make Noise O Coast, which apparently responds to ALL channels, when it should only respond to one (on the Mod Wheel). What a lifesaver!

    I’ll be checking out more of your pages, and starting more Arduino projects!

    Like

    1. That is great to hear! Thanks for dropping by and letting me know.

      Do continue to explore and feel free to give me a shout if you have any comments or suggestions.

      I’m glad something here was useful.

      Best wishes,
      Kevin

      Like

  3. Hello Kevin, I’m not really sure I understand how the filtering works. I was hoping to use your program to filter out only the stop start messages sent from a master clock and allow everything else to pass through. This is so that FX pedals and loopers can continue in sync with the clock but stay unaffected by any stop start messages. Can you help me there please? Can your code be easily modified to accommodate this?
    regards
    Robbie

    Like

    1. If I understand you correctly, I think you are wanting to stop all System real-time messages for start sequence (0xFA) and stop sequence (0xFC) – and presumably continue (0xFB) as well? – is that what you mean? At present the filter code only lets through NoteOn and NoteOff messages. Adding other standard channel messages is relatively straightforward, these are just more cases in the loop().

      Passing through system common and system real-time messages in general is probably ok, but it doesn’t handle system messages of any kind at present and handling SysEx at all is a bit more complicated.

      I don’t know if the performance of the Arduino would be up to a lot of data being passed through like this though – you’ll have to see how you go I guess?

      If you don’t need SysEx, then the following might do it (untested):

      void loop() {
        if (MIDI.read()) {
          byte ch = MIDI.getChannel();
          byte cmd = MIDI.getType();
          switch (cmd) {
            case midi::SystemExclusive:
            case midi::Start:
            case midi::Continue:
            case midi::Stop:
              // Ignore these messages
              break;
            default:
              // Pass through everything else
              digitalWrite (MIDI_LED, HIGH);
              MIDI.send(MIDI.getType(), MIDI.getData1(), MIDI.getData2(), MIDI.getChannel());
              digitalWrite (MIDI_LED, LOW);
              break;
          }
        }
      }

      If you need to pass through SysEx, then it will probably need some special handling which I’d have to work out, but I’ve not done anything with SysEx before.

      If your pedals are using different commands to start/continue/stop, then it is probably more complex to untangle and do…

      Do let me know how you get on if you manage to try any of this.

      Kevin

      Like

Leave a comment