Arduino Touchscreen Mini MIDI Tenori-On

The next experiment with my cheap touchscreen is to re-create my mini Tenori-On, but this time with more “buttons”.

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

The Circuit

This is the same circuit as with my previous touchscreen MIDI controllers – an Arduino Uno with the touchscreen and a MIDI module.

IMG_5444

The Code

A good proportion of the code comes from the first touchscreen MIDI controller – the one with the 6×6 grid of “buttons”.  The code is expanded to support a larger grid of buttons, and an asymmetrical display.  This turned up one bug with the grid handling.  I knew I’d get one of the rows/columns calculations mixed up when both rows and columns were the same number – 6!  I’ve updated my previous code, so now it should be fine.

This is the display I’m aiming for:

IMG_5443

My previous Tenori-On was written in Micropython for my Raspberry Pi Pico, but the basic idea is the same.  We need a “scan” across the display, playing each activated set of notes, one column at a time.

The most typical Tenori-On style application has all buttons on the same row playing the same note.  I’ve kept that functionality, but I’ve still used a “note per button” configuration array at the start of my code for complete flexibility.  I’m using a pentatonic scale again, so my grid of notes is as follows.

const PROGMEM byte notes[NUMBTNS] = {
// Pentatonic C3-C4
60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,
57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57,
55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55,
52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52,
50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
};

As this array is starting to get quite large now, I’ve stored it in the Arduino’s PROGMEM.

I’ve enhanced the touchscreen handling slightly to include the option to crudely scale the values to better fit the screen – I had to do this for my second MIDI controller too.

The touchscreen handler will simply toggle the buttons on the display and the MIDI handling in the main loop will do the “scanning” of the grid and respond as required.  I’ve included a “debounce counter” that means that the screen won’t respond immediately after a button being touched.  It’s crude but it lessens the sensitivity enough to be useful I think.

int debounce;
void gfxLoop() {
if (debounce > 0) {
debounce--;
return;
}
if (ts.touched()) {
debounce = 50;
TS_Point p = ts.getPoint();
int btn = xy2btn(xt2xdsp(p.x), yt2ydsp(p.y));
if (btn != -1) {
if (btns[btn]) {
gfxBtnOff (btn);
btns[btn] = 0;
} else {
gfxBtnOn (btn);
btns[btn] = 1;
}
}
}
}

In addition to my existing gfxBtnOn() and gfxBtnOff() routines, there is now a gfxBtnPlay() and gfxBtnStop().  The former lights up the buttons in the row being played, and the latter returns them to the state they were in before (on or off).

The actual Tenori-On logic happens in the Arduino’s loop(), but it only runs after a certain elapsed time that represents the tempo of the beat.  The main loop has the following functionality:

loop():
gfxLoop()

IF not time for the next beat THEN return

FOR each note in the previous step's column:
turn off the graphic button with gfxBtnStop()
send a MIDI noteOff

gfxLoop()

FOR each note in the next step's column:
send a MIDI noteOn
turn on the graphic button with gfxBtnPlay()

There are a few design choices worth bringing out here:

  • I call the gfxLoop() between the off and on handling to keep the responsiveness of the touchscreen sensible.  It takes a while to redraw a whole column of buttons.
  • In the code I use two loops, one for the graphics and one for the MIDI notes.  This means that the MIDI notes happen almost at the same time.  If I put them in the same loop, each MIDI note would be delayed by the time it takes to redraw that buttons and then when you select the top and bottom button in a column you can hear the delay!

I’ve kept in some of the test code that will print out a range of coordinates for the touchscreen and buttons which can be used for some crude calibration.

Find it on GitHub here.

Closing Thoughts

I really wanted to use the flexibility of the touchscreen to give me many more buttons than my physical mini Tenori-On allowed, and to a certain extent this does that.  The touchscreen isn’t as accurate as I’d like. You can see in the video a few times where the screen hasn’t responded and once or twice where it highlighted the wrong button, but on the whole it’s pretty usable.  I expect this is tweakable in the code now that the basics are there.

In the video it is driving my MT-32 using the “schooldaze” voice.

Kevin

 

Leave a comment