TFT MIDI Display – Part 2

Building on my previous project adding a colour display to an Arduino, I thought I’d use the graphics a little by adding one of those “show the note on the stave” displays you quite often get on modern keyboards.

  • In Part 3 I preset an alternative visualisation.

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

The Circuit

This is using the same circuit as described in TFT MIDI Display.  I’m using my “shield” for mounting the display and have hooked it up to my USB MIDI module so I can use my Korg Minikeys as usual.

The Code

Once again, the Adafruit graphics library GFX gives us everything we need to get a nice graphical display up on the screen.  This time I’m using the shape-drawing functions and will be using a bitmap to place clefs on the stave.

This is the blank stave ready for action.

2020-10-25 13.55.20

The basic idea is that I’ll use MIDI note messages to draw the appropriate note on the stave, so the gaps between the lines are a known number of pixels apart.  Middle C is the same distance from each stave too.  Of course it gets complicated when we get to black notes.

I’ve opted to display sharps, so I need a way of positioning the sharp in the right place.  It also complicates the calculation of note position as following the pattern of the music keyboard there isn’t a sharp every two notes, so calculating positions isn’t so easy.

Rather than doing it all using calculations and logic, I’ve opted for two techniques that make it a lot easier to do:

  • I have several “look up tables” for each note.  These determine the following: if the note is a “black note”, hence requires a sharp sign; if the note requires a ledger line – this applies to middle C, E below the stave, and A above the stave; and some indication of the relative position of the notes to the top of the stave – to allow for both a note and its sharp to occupy the same y-coordinate on the display.
  • There are lots of definitions at the start relating to positions of the notes, the clefs, the sharps and the stave itself.  These were set largely using some educated guess work and a bit of trial and error. These will very much depend on the size of the clefs used and spacing of the stave.

To get the clefs and a sharp sign, I use hard-coded bitmaps creating using Paint and this website: http://javl.github.io/image2cpp/.  This creates a chunk of Arduino code that can then be passed into the Adafruit GFX library routine drawBitmap, as shown below:

// Bitmap for the sharps
#define SHARP_W 14
#define SHARP_H 17
// 'Sharp', 14x17px
const uint8_t sharp [] PROGMEM = {
  0x00, 0x60, 0x0c, 0x60, 0x0c, 0x60, 0x0c, 0x60, 0x0c, 0x7c, 0x0f, 0xfc, 0x7f, 0xe0, 0x7c, 0x60, 
  0x0c, 0x60, 0x0c, 0x7c, 0x0f, 0xfc, 0x7f, 0xe0, 0x7c, 0x60, 0x0c, 0x60, 0x0c, 0x60, 0x0c, 0x60, 
  0x0c, 0x00
};

....

 tft.drawBitmap (sharp_x,sharp_y,sharp,SHARP_W,SHARP_H,ST77XX_BLACK);

The bitmap will only update any pixels that are “set” and won’t touch any that are cleared.  This means all set bits are set to the provided colour (black in the above example) but all the rest of the pixels are left as they were on the screen.  This means that erasing the sharp can be done by using the same function, just setting the colour to white and then drawing back in any stave lines that were rubbed out as a consequence.

I’ve experimented with a range of ideas for how to draw notes in response to a MIDI note on and then rub them out in response to a MIDI note off.  The final idea which seems to work ok is loosely as follows.

  • There is an “active note list” that is maintained in response to MIDI note on and note off messages.  This unlinks the drawing of notes from responding to MIDI messages.
  • Active notes are redrawn as part of a “scan” of the display.  If this is done every loop() then it gets very laggy responding to MIDI, so it is only done if something has changed – i.e. a note has been drawn or cleared.
  • Notes are individually rubbed out on receipt of a MIDI note off message.  This will result in some of the background (stave, other notes) also being rubbed out, but I rely on the redrawing of the active notes to repair things again.
  • Some notes also require a ledger line adding (and consequently rubbing out too).
  • Each note is drawn as a pair of filled circles a couple of pixels apart on the X axis, which creates a nice filled oval shape.
  • The lines on the stave and ledger lines are drawn using the drawFastHLine function which is optimised for horizontal lines only.
  • Sharps are drawn using the bitmap functions as already mentioned.

Initially I had all notes (plain and sharps) drawn along the same Y coordinate.  This looks great for chords and simple melody lines, but if several notes are clustered together, it isn’t possible to tell the difference between a note and its sharp being played at the same time.  Because of this the sharp notes are now displayed in a separate column to the notes.

Find it on GitHub here.

Closing Thoughts

I’m really pleased with the final result here.  It took a fair bit of trial and error to get the positions of notes, sharps and the stave working ok, and I went through four or five different approaches to drawing and re-drawing notes in response to MIDI messages.

The musician in me would like to be able to set a key signature and have the notes displayed be the correct diatonic interval for the key signature.  Sharps are simple but it does grate a little to keep seeing A# rather than Bb all the time.

It also be nice to add some colour.  One idea could be to use colour for notes in specific chords or certain accidentals. It would also be interesting to try to add support for more than one ledger line and possibly even alternative clefs.

The MIDI channel is set within the code.  One of these days I’ll find a simple hardware way to indicate MIDI channel for builds such as these.

Kevin

Leave a comment