The Akai EWI uses a set of touch-sensitive metal keys:
A key is actuated simply by touching it. The instrument detects the change in capacitance that results when your body touches the key. For more information on how this works, see https://en.wikipedia.org/wiki/Capacitive_sensing. And for some videos of people playing the EWI, look on YouTube. I especially like this one of the late Michael Brecker: https://www.youtube.com/watch?v=tPUBp9uTLIw
Can I Do This?
Is there a way to inexpensively build this into our own wind controllers? The answer is definitely yes.
NXP Semiconductors manufactures a very low-cost chip, the MPR121 ($1.95 in single quantities), that provides 12 separate touch inputs.
and Adafruit Industries has a nice breakout board that breaks out the tiny pins on the chip, and has some support circuitry that allows use with either 3.3v or 5v controllers.
Sparkfun also has a breakout board for the MPR121. It doesn't include the level shifters to allow 3.3/5v operation, but is less expensive.
I won't write a tutorial on how to wire up the sensor and use it, since the Adafruit tutorial does a great job of that. Instead, I'll show how we can incorporate it into a woodwind-style controller.
One thing that may occur to you is that there are only 12 inputs, but typical woodwinds have more than 12 keys. Is there a way to use more than one touch sensor chip in an instrument? Yes, there is. The MPR121 is an I2C device, which means you can attach more than one to the same two-wire communication bus, as long as they have different addresses. Both the Adafruit and Sparkfun breakouts include a way to set the address (up to 4 different addresses can be set), so it is possible to do touch-sensing on up to 48 keys. That should be enough.
Modifying get_note() For Touch Sensing
In my post, Note Selection Basics, I define a function named get_note() that read the switches that simulated our "trumpet valves" and returns the MIDI note to play. Let's replace that with a new function that can read the MPR121 and detect which of the 12 inputs is being touched.First, a little background. Adafruit provides a library for the MPR121 that makes configuring and reading it very simple. To initialize the chip:
touchSensor.begin(0x5A);
(0x5A is the default I2C address of the Adafruit breakout)
Then, to read all of the pins:
uint16_t touchValue = touchSensor.touched();
This will return a 16 bit value where each of the lower 12 bits is a 1 if the key is touched, and a 0 if it is not. Which bit corresponds to which key depends on how I wired the brass washers to the touch sensor input pins. In my case, the octave keys are mapped to bits 0 and 1, and then the rest of the keys are:
LH index finger: bit 2
LH ring finger: bit 3
LH middle finger: bit 4
(bit 5 not used - it's an extra key on my instrument not currently used)
RH index finger: bit 6
RH ring finger: bit 7
RH middle finger: bit 8
RH pinkie: bit 9
(bits 10, 11 not used)
Table Lookup for Note Mapping
As I mentioned in the Note Selection Basics post, a C language case statement will start to get pretty ugly for an instrument with all these keys, so let's look into a table lookup approach. We'll build a table that has, in one column, the expected bit values read from the touched() function of the MPR121, and in the next column, the MIDI note to send.In C, we can define a structure that holds one row of that table like this:
struct fmap_entry {
uint16_t keys;
uint8_t midi_note;
};
This is a chunk of memory that can hold a 16-bit value (named "keys", which will hold a bitmap of key values), and an 8-bit MIDI note value.
Next, we'll build an array of these:
#define FMAP_SIZE 33
struct fmap_entry fmap[FMAP_SIZE];
This defines an array (or table, if you prefer) of 33 rows of the structure we define above.
To map from a fingering to a MIDI note, we start at the beginning of this table and check to see if the fingering we just read from the sensors matches the value in the "keys" field. If it does, we've found the MIDI note and we're done. Otherwise, we skip to the next entry, and so on. In programming, this is called a linear search.
We actually need to initialize the "fmap" array with all of the key and note values. That's very verbose, so I'll omit it here and will just include it with the full code.
So here's the revised get_note() function. Since my instrument has an extra key I'm not using, there's a little code (in blue) that makes sure that even if the player touches that unused key, it won't change the value we read, so we don't need to have entries in the table for when that key is touched/not touched.
int get_note() {
// This routine reads the touch-sensitive keys of the instrument and
// maps the value read to a MIDI note. We use a lookup table that maps
// valid combinations of keys to a note. If the lookup fails, this
// routine returns -1 to indicate that the fingering was not valid.
int ret = -1; // Sentinel for unknown fingering
uint16_t touchValue = touchSensor.touched();
// Since we're not using the 4th finger of the left hand, mask off that key
touchValue = touchValue & 0b1111111111011111;
for (uint8_t i = 0; i < FMAP_SIZE; i++) {
if (touchValue == fmap[i].keys) {
ret = fmap[i].midi_note;
break;
}
}
return ret;
}
The for loop (in red) is where we do the linear search. We look at each keys value, and when we find a match, we stop looking and return the corresponding MIDI note.
If we don't find a match, then the player has their fingers in a non-supported position, and our function returns -1, which means to take no action. In other words, we ignore the glitch. That's not the only thing we could do. If, for example, we wanted to try to mimic how a real instrument behaves, we might send a pitch bend message to indicate that the pitch should be a little sharp or flat relative to the normal fingering. There are a lot of possibilities.
Ok, now that we've had a look at some code, is it possible to build something?
A Test Platform
I've built a very hacky prototype woodwind-style controller as a platform to test these ideas. The body of the instrument is a 14" length of PVC plumbing pipe, 2" or so in diameter. I was going for roughly the dimensions of an alto recorder, but chose a thicker tube to make running wires inside a little easier.To make the touch-sensitive keys, I soldered lengths of wire to ten brass washers:
Then I drilled holes in the tube, ran the wires from the outside to the inside, and hot-glued the washers to the outside of the tube, about where my fingers would fall. I didn't arrange the keys to resemble any particular instrument, although they're somewhat like a recorder layout (albeit with one extra key for the left hand, oops). The Frankenstein-like contraption looks like this:
The white mouthpiece protruding from the top is something I made to use with my Blowchucks Controller for the Electro-Music Festival in 2014. It connects to a tube that runs down the length of the instrument and connects to the Freescale pressure sensor I've been using in all of my wind controller projects.
On the back, I added two octave keys, and strapped all of the electronics near the base. This means I only have a USB cable running from the instrument to my laptop.
In this close up of the electronics, you can see the MPR121 touch sensor breakout on the left. The green and white wires that attach to that board are the wires coming from the brass washers. The board to the right has the Teensy microcontroller mounted on the right, the the pressure sensor on the left. You can also see the power and ground (red/black) wires going to the touch sensor, as well as the two wires (yellow/white) that connect the touch sensor to the I2C bus on the microcontroller.
I'll do a proper video later, but for now, here's a quick audio demo of the instrument running the code below. You'll hear some glitches as I'm not a woodwind player and have a hard time getting multiple fingers to touch keys at exactly the same time. We can de-glitch that stuff in code, and I'll work on that for a future post.
The full code is posted below, and is also available at https://github.com/ggood/NoteSelectionTutorialRecorder