Wednesday, January 13, 2010

The Trombone Controller Sings - accompanied by $38 worth of fail

Tonight I made another baby step with the trombone controller. I got the slide sensor (see FAIL* below) and the breath controller both working, mounted on the instrument, and added an RJ-45 connector so I can hook the thing up to the Arduino via an Ethernet cable, unclamp it from the workbench, and pick it up and play it. So, for the first time, I was able to get a feel for how playable this thing will be.

I was pleasantly surprised. While there are a lot of glitches, I felt like I was able to be expressive on the instrument. Here's a video:



The whole air pressure system is made from garden drip irrigation tubing I had laying around. I really hope it's non-toxic.


*FAIL (or, how to blow up a $27 linear potentiometer and an $11 pressure sensor)

In my quest to make all the pieces of my new instrument modular, I decided that I would terminate each sensor in some sort of connector that could be plugged/unplugged to assemble new, unanticipated instruments. Well...
  • Don't use 1/8" stereo phono jacks for anything carrying Vcc. They short as they're being plugged/unplugged, which (thank you, Arduino designers) doesn't make the magic smoke come out of the Arduino, but it does make your Mac grumpy with the excessive current on the USB bus - but no Mac damage either (thank you, Mac designers).
  • Measure (er, check wiring) twice. Or maybe 5 or 6 times. I managed to wire 5v across the wrong two leads of my quite expensive SpectraSymbol SoftPot, and now when no pressure is applied, the output lead floats. Well, at least Mouser Electronics gets something out of this. :-(
  • Freescale Pressure Sensors don't care that your low-wattage soldering iron sucks and won't melt solder and you pulled out the big iron to solder the sensor to a perfboard. Moral of the story: socket everything in a prototype!

Sunday, January 10, 2010

Trombone Controller - Prototype Taking Shape

This weekend, I had two thoughts about the design of my trombone controller, while watching Media Lab's Podcast (LabCast) about the Chameleon Guitar.

Thought 1: While the instrument they built is very different from the one I'm building, what struck me was the statement:

"Computers have radically changed our lives over the last fifteen years. However, the design of the electric guitar has hardly changed in the last fifty. Is this because musicians respond more to tradition, than to innovation, or, is it because the right interface has yet to be developed? (emphasis mine). The project argues that combining the unique traditional values of established musical instruments with modern technology can open the door to a myriad of new opportunities.

This resonated well with me, and it's encouraged me to finally write down a "manifesto" that captures my ideas about musical instrument design (the last three points are more about open source).

The instruments I design should:
  • reward the mastery of the performer. It should be possible to become a virtuoso on one of the instruments I design.
  • translate gesture well for skilled performers. This might mean that an instrument is idiomatically "correct" for a performer familiar with a traditional instrument. Or it might mean that a musically skilled person can instinctually control my instrument in a way that is pleasing both to the performer and her audience.
  • be reproducible by others. This means that all software and hardware will be open source.
  • encourage further experimentation and extension.
  • not impose any restrictions on their use. I want performers of instruments of my design to be able to do anything they wish with the music they make.
Thought 2: There is a lot of value in making the instrument beautiful to look at and pleasing to touch.

This thought occurred to me while watching the section in the Media Lab video where they describe how they collaborated with a string instrument builder to build the neck and body shell of the instrument.

My first thoughts about my instrument's design centered around metals, which makes sense when you consider that I spend years blowing air into a hunk of brass. But once I saw the Media Lab video, it occurred to me that there's no really good reason that I couldn't build the thing out of wood. And, given that, there's no reason I couldn't find someone with woodworking skill to make the final product really beautiful, and furthermore, there's no reason I couldn't make an interesting prototype myself, using some simple tools.

So, this afternoon I went to Home Depot and bought a 6-foot long section of 1 x 2 pine, a 3/4" dowel, and a SurForm tool, and stopped at Fry's Electronics and bought a connector I thought would allow the SpectraSymbol sensor to plug in (soldering directly to the leads would certainly destroy it).

Then, this evening I spent about an hour forming the 1x2 into the "slide" of my trombone controller prototype out of that 1x2. The slide ends up looking more like the fingerboard of a string instrument than a trombone slide(see photo). The player places his/her index finger on the top, where the sensor is, and the thumb underneath. Here's a (not very good) photo of the result, clamped to my workbench for testing (click to view a larger image):




The SpectraSymbol sensor is great - only a very light pressure is required to maintain a reading, and it seems to have very little jitter when I run a sketch that prints the values on an analog input pin.

I then hooked up the set of momentary switches I use as a temporary overtone selector, and tried to play a major scale. It's glitchy, but clearly recognizable as an ascending/descending scale. I'll be addressing the glitchiness later in software.

Next up: get the breath sensor attached to the prototype, and then figure out how to let the player control the overtones with the left hand.

Wednesday, January 6, 2010

We have a winner!

In the quest for a pressure sensor with an appropriate range for my prototype wind instrument controller, the winner is...

The Freescale MPXV4006GP (pdf spec sheet)

With my blow-tube from experiment 6 hooked up to this sensor, through a T connector with half the flow going to the sensor and half dumped, the zero to full response feels pretty natural to me (although I haven't played my trombone in a looooong time). The raw values read from the sensor are about double those of the sensor I used in Experiment 6.

Physical Interfacing

The sensor comes in a package designed for SMD mounting, so I straightened the legs and soldered it to some dual headers.

To hook up the air supply, I attached some heat shrink tubing (unsure of the diameter) to the input port and gently heated it to get a seal.




This tubing, it turns out, has an outer diameter very close to the inner diameter of 1/4" garden drip irrigation tubing, which is what I hooked up to my blow-tube. So sliding the irrigation tubing over the heat shrink and overlapping for a couple of inches makes a good-enough seal.





Electrical Interfacing

Easy:
  • +5V to pin 2
  • Ground to pin 3
  • pin 4 to analog input of Arduino. Raw input values from the Arduino ADC range from 58 (no blowing) to 1015 (blowing like I'm playing trombone on Varese's Integrales).

Tuesday, January 5, 2010

Experiment 6 - Freescale MPX5010GSX Sensor

My initial experiment with using a pressure sensor for breath pressure detection didn't turn out so well, because the sensor I chose responded to a much higher pressure than I could produce. So I ordered a couple of lower pressure sensors to experiment with. The first one I'm trying is a Freescale MPX5010GSX. It costs more ($14 from Mouser) than the previous one I tried but it's not so expensive as to be unreasonable.

The sensor comes in a nice 6-pin package with leads that will plug right into a breadboard. Pin 1 has a little notch in it, so it's easy to identify. Pin 1 is the output, Pin 2 is ground, and pin 3 is a 5v supply voltage. Pins 4, 5, and 6 are labeled as V1, V2, Vex, but I'm not sure what those do. I left them floating.

To drive the input port, I created a "mouthpiece" of a 6-inch length of one-inch vinyl tubing (bought at the local Ace Hardware). I folded over one end and clamped it shut with a couple of clamps. Then I used an awl from an irrigation tubing kit to open a hole near the clamped-off end and attached a T-connector for 1/4" irrigation tubing. I attached one end of the T to the pressure sensor through a couple feet of 1/4" tubing (it fits perfectly over the sensor), and ran the other end of the T through about a foot of 1/4" tubing. This will be the "blow through" for the sensor, and by clamping it off partially, I can control how much air goes through the instrument.

Here's a picture:



The pressure sensor is on the breadboard at the bottom of the photograph. The tube to blow into is on the right, as is the dump tube. The orange clamps close off the end of the blow tube, forcing all of the air into the tubes that flow (a) to the pressure sensor and (b) to the dump tube.

I then wrote a small Arduino sketch that printed the values on analog port 2 every 100 milliseconds and ran it.

(Sketch)


/*

Print values received from a Freescale MPX5010GSX pressure sensor

Gordon Good (velo27 yahoo com)

*/
const int SENSOR_PIN = 2; // Sensor Vout on analog input 2

void setup() {
Serial.begin(9600);
}


void loop() {
int pVal = analogRead(SENSOR_PIN);
Serial.println(pVal);
delay(100);
}



Results:

With the dump port completely closed off, and blowing my brains out, the highest reading I can get is about 1015, not much below the maximum sensor value of 1023. With the dump port wide open, the max value is about 650. Wide open feels pretty good to me in terms of how hard I'm blowing, so I think we're in the ballpark with this sensor.

So, in other words, a comfortable "loud blow" for me, with a comfortable amount of air dumped, produces a value of about 1/2 scale on a 10 kilopascal sensor. Assuming the response is linear, the pressure I'm producing is about 5 kilopascals.

Thursday, December 31, 2009

Experiment 5 - testing electronics for a Trombone Controller

For a long time now I've been thinking of how I could build an electronic wind instrument that would be easy for a trombone player to pick up. Although I've owned a WX-7 and have played around with the Akai EVI and EWI, I've never had the time/patience to get the finger chops needed to enjoy those instruments.

Once I found out about the Arduino and the various types of sensors that are available cheaply, it seemed like the right time to get serious about designing a trombone controller.

My current working plans are to use three sensors:
  • A breath sensor that controls articulation and volume.
  • A position sensor of some kind that can detect the slide position.
  • A set of switches, or a position sensor that controls which overtone is selected. I envision that this will be actuated with the player's left thumb, where the F-attachment lever is usually found.
In other words, pretty much like an Akai EVI/Steinerphone, except that we replace the valve buttons with a slide, and we replace the pitch rollers + drop-a-fourth button with the overtone selector.

There are a lot of different possibilities for what kind of sensors to use for each of these, and I'll need to do some experimentation to find what works best. But I don't need to figure that out before I can start working on the code - the Arduino lets me abstract away those issues. So, really, we have:
  • An analog input for the breath sensor.,
  • an analog input for the slide position, and
  • a set of switches for selecting the overtone.
Regarding the code: there are two main paths I might choose:
  1. Make the whole thing a MIDI controller, and have the Arduino send MIDI to a synthesizer (or computer + softsynth, etc).
  2. Use something like Max/MSP or pd to read the controllers directly and control synths implemented in software on the Mac/PC.

The advantages of #1 are:
  • You can take advantage of any existing synth patch, although you do have the problem every wind controller player has in finding/creating patches that respond well.
  • This approach is faster for me to implement, since I don't know Max/MSP.
The advantages of #2 are:
  • You have more control of how the synth responds to the instrument.
  • You aren't limited by MIDI's coarse-grained values (if those are in fact a problem).
The nice thing is that I'm not limited to one or the other approach. All you need is different firmware on the Arduino (heck, just get another one and plug into whichever one you want to use).

So I decided to start down the MIDI path. Just to get started, I built the following circuit to prove some concepts:



The "slide" is a Sharp Infrared sensor, the "breath controller" is a force sensing resistor, and the overtone selector is 5 separate momentary pushbutton switches.

I wrote a sketch that reads the values of each sensor/switch in a loop. For this first iteration, I decided to focus on making the overtone selector and the slide work well, and not worry so much about when note on/off messages are sent. So this thing is always "on", and a new note is only started when a different register button is pressed.

I also opted in this iteration to use pitch bend messages exclusively for pitch within a given overtone. In other words, the only note on values we ever send are the fundamental, the first overtone, the second overtone, etc. All the notes in between are produced by sending a downward pitch bend message. This requires that the synth be set so that maximum bend is six semitones, corresponding to the seven positions of the trombone slide.

On a breadboard, obviously, you can't twiddle all these controllers at once, so I first verified that the overtone selector worked properly. I then checked that tapping/holding/squeezing the force sensor produced something like a reasonable articulation and volume changes. Then I just set up the firmware to send a constant value instead of reading the force sensor (I only have two hands) and verified that the "slide" did something reasonable.

Here's a short sample:




   
   
   
   
   


In the first part, I'm moving up and down the overtone series with the slide stationary, and in the second part I "glissando" down from three diffrerent overtones.

In summary, this all looks promising. Next steps:

  • Find a pressure sensor to use for a breath controller. The first one I tried required far too much pressure.
  • Figure out how to physically make the slide, and experiment with different sensors. Some other ideas include using nichrome wire as a sensor, and position-sensing resistors (these are expensive in the size I need - about 2 feet long).
I'm mostly a software guy, so actually making the physical instrument will be a big challenge.

Here's the sketch.


/*

Prototype sketch for a trombone-like MIDI controller based on the Arduino hardware.

Gordon Good (velo27 yahoo com)

Hardware:

- An overtone selector. At this point, it's a series of discrete on-off pushbottons.
This replaces the act of blowing overtones an a physical instrument.

- A "slide". Currently, this produces pitch bend information, and is implemented
using an infrared distance sensor.

- A volume controller, intended to be actuated by the player's breath.


*/
#include "Midi.h"

const boolean DEBUG = false;
//const boolean DEBUG = true;

const int FSR_PIN = 0;
const int IR_PIN = 1;
const int OT_0_PIN = 2;
const int OT_1_PIN = 3;
const int OT_2_PIN = 4;
const int OT_3_PIN = 5;
const int OT_4_PIN = 6;

const int FUNDAMENTAL = 36; // MIDI note value of our fundamental
const int OT_1 = 48; // First overtone
const int OT_2 = 55; // Second overtone
const int OT_3 = 60; // Third overtone
const int OT_4 = 64; // Fourth overtone
const int OT_NONE = -1; // No overtone key pressed

const int MIDI_VOLUME_CC = 7;

long ccSendTime = 0; // Last time we sent continuous data (volume, pb);
const int MIN_CC_INTERVAL = 10; // Send CC data no more often than this (in milliseconds);

Midi midi(Serial);

int playingNote = -1;

void setup() {
if (DEBUG) {
Serial.begin(9600);
} else {
midi.begin(0); // Initialize MIDI
}
}

int getPitchBendFromForceSensor() {
// Get the raw value from the force sensor
int pbRawVal = analogRead(FSR_PIN);
// Constrain it to values we're willing to handle
int pbConstrainedVal = constrain(pbRawVal, 0, 1023);
// Then map those values to the MIDI pitch bend range values.
return map(pbConstrainedVal, 0, 1023, 0, 16383);
}

int getPitchBendFromIRSensor() {
// Get the raw value from the IR sensor
int pbRawVal = analogRead(IR_PIN);
// Constrain it to values we're willing to handle
int pbConstrainedVal = constrain(pbRawVal, 50, 640);
// Then map those values to the MIDI pitch bend range values.
// NOTE: We stick our head in a hole here and assume the IR
// sensor response is linear. In fact, it's not, so we need to
// come back and improve this. The current implementation will
// result in a trombone slide that is non-linear.
return map(pbConstrainedVal, 640, 50, 16383 / 2, 0);
}

int getNeutralPitchBend() {
return 8192;
}

int getPitchBend() {
return getPitchBendFromIRSensor();
}

int getMIDINote() {
// middle C = 60, so let's use two octaves below middle c as the fundamental of this instrument for now
if (digitalRead(OT_4_PIN) == 1) {
return OT_4;
} else if (digitalRead(OT_3_PIN) == 1) {
return OT_3;
} else if (digitalRead(OT_2_PIN) == 1) {
return OT_2;
} else if (digitalRead(OT_1_PIN) == 1) {
return OT_1;
} else if (digitalRead(OT_0_PIN) == 1) {
return FUNDAMENTAL;
} else {
// No overtone key pressed - return -1 so caller can know
return OT_NONE;
}
}

int getFixedVolume() {
return 127;
}

int getVolumeFromFSR() {
// Temporary code, since I don't have a good breath
// sensor yet. However, initial tests with a force sensing resistor
// indicate that it's possible to do decent articulation with the
// following code.
int volRawVal = analogRead(FSR_PIN);
return map(volRawVal, 0, 970, 0, 127);
}

int getVolume() {
return getFixedVolume();
}

void sendNoteOn(int note, int vel, byte chan, boolean debug) {
if (debug) {
Serial.print("ON ");
Serial.println(note);
} else {
midi.sendNoteOn(chan, note, vel);
}
}

void sendNoteOff(int note, int vel, byte chan, boolean debug) {
if (debug) {
Serial.print("OFF ");
Serial.println(note);
} else {
midi.sendNoteOff(chan, note, vel);
}
}

void sendPitchBend(int val, boolean debug) {
if (debug) {
Serial.print("BEND ");
Serial.println(val);
} else {
midi.sendPitchChange(val);
}
}

void sendVolume(int volume, byte chan, boolean debug) {
if (debug) {
Serial.print("VOL ");
Serial.println(volume);
} else {
midi.sendControlChange(chan, MIDI_VOLUME_CC, volume);
}
}


void loop() {
int pb = getPitchBend();
int note = getMIDINote();
int volume = getVolume();
if (-1 != note && note != playingNote) {
sendNoteOff(playingNote, 0, 1, DEBUG);
sendPitchBend(pb, DEBUG);
sendVolume(volume, 1, DEBUG);
sendNoteOn(note, 127, 1, DEBUG);
playingNote = note;
delay(50);
} else {
if (millis() > ccSendTime + MIN_CC_INTERVAL) {
sendPitchBend(pb, DEBUG);
sendVolume(volume, 1, DEBUG);
ccSendTime = millis();
}
}
}

Monday, December 28, 2009

Experiment 4 - pd and arduino

I've been thinking about a platform for building musical instruments made of out physical controllers, and one plausible option is to use the Arduino as a data gathering device feeding into PureData (pd). There is a firmware project (Firmata) that turns the Arduino into a data gathering device for pd, and a pduino project for pd that provides pd objects that can read from and write to the Arduino.

I haven't played with pd very much, so I'm only scratching the surface here, but I assembled a little instrument that responds to the ambient light in the room by altering the pitch of a sine wave oscillator. Of course, I couldn't see fit to leave well enough alone, so I added a bit of FM synthesis (based on the pd tutorials), whose parameters are also controlled by the ambient light, so this is sort of a twisted theremin.

I can't figure out how to record the audio output of pd (in the 30 minutes I spent trying to figure it out), so there's no recording of this experiment. The circuit is very simple - a voltage divider to measure the voltage drop across a photoresistor, connected to analog input 0 on the Arduino. Here's a screen capture of the pd patch.



I got a little tricky where the sampled output of the photoresistor feeds the oscillator input frequency, but also feeds the amplitude and frequency of the FM component of the sound (for more information on FM synthesis, see the "A09.frequency.mod.pd" section of the PD help files. It sounds like a theremin with a "send-back-to-factory" problem.

Thursday, December 24, 2009

Experiment 3 - Distance Sensor Pentatonic Wash

In Radio Shack this afternoon, I found that they had an ultrasonic distance sensor (actually a Parallax ping))) sensor) for sale. I bought one, and put together a simple instrument.

To use the Parallax, you send a pulse by asserting its input high for a few microseconds, then measure the duration of the pulse it returns (which corresponds to the distance to the target). While that may sound complicated, the Arduino library provides a simple function that will measure the duration of a pulse, so this only requires a few lines of Arduino code.

This instrument measures the distance from the sensor to the object (typically the performer's hand) and plays a note (where the pitch corresponds to the distance) for two seconds. It repeats this cycle 3 times per second, so three new notes are started every second, and three notes are stopped each second. The notes overlap, and this produces a pleasing "wash" of notes. The pitches are selected from a pentatonic scale (the black keys on a piano). Here's what it sounds like:









And here's a video of how it's played:


And the code:


/*
Produce a pentatonic wash with pitches based on how close the performer's
hand is to a Parallax ping))) ultrasonic distance sensor.

The basic idea is to sample the distance sensor every 1/3 of a second, and
transmit a note on event corresponding to where the performer's hand is
relative to the sensor. At the same time, add to an event queue a note off
event 2 seconds in the future to turn off the note. This causes a "wash"
of sound as the new notes overlap with those already sounding.

The Parallax sensor should be connected to pin 7 on the Arduino.

Gordon Good (velo27 yahooo com) 12/24/2009.

*/

#include "MIDI.h"

class Event {
public:
int type;
int value;
long time;
};

// Pitches of a pentatonic scale, in 7 different octaves.
int notes[] = {
40, 42, 45, 47, 49,
52, 54, 57, 59, 61,
64, 66, 69, 71, 73,
76, 78, 81, 83, 85,
88, 90, 93, 95, 97,
100, 102, 105, 107, 109,
112, 114, 117, 119, 121,
};

const int PING_PIN = 7;

const int QUEUE_LENGTH = 100;
const int NOTE_ON = 1;
const int NOTE_OFF = 2;

Event currentEvent;
Event queue[QUEUE_LENGTH];
int queue_head = 0;
int queue_tail = 0;

int enqueue(int type, int value, long time) {
queue[queue_tail].type = type;
queue[queue_tail].value = value;
queue[queue_tail].time = time;;
queue_tail = (queue_tail + 1) % QUEUE_LENGTH;
}

void dequeueMIDIMessage() {
while (queue[queue_head].time != -1 && queue[queue_head].time < millis()) {
if (queue[queue_head].type == NOTE_ON) {
// Not used in this sketch
MIDI.sendNoteOn(queue[queue_head].value, 127, 1);
} else if (queue[queue_head].type == NOTE_OFF) {
MIDI.sendNoteOn(110, 127, 1);
MIDI.sendNoteOff(110, 0, 1);
MIDI.sendNoteOff(queue[queue_head].value, 0, 1);
}
queue[queue_head].time = -1;
queue_head = (queue_head + 1) % QUEUE_LENGTH;
}
}

int ping_getEchoDuration() {
// To measure dstance with the PING))), send a pulse of 5 uSec, then
// read the duration of the pulse it sends. To send a clean pulse,
// set the output low for 2 uSec, then send the pulse.
pinMode(PING_PIN, OUTPUT);
digitalWrite(PING_PIN, LOW);
delayMicroseconds(2);
digitalWrite(PING_PIN, HIGH);
delayMicroseconds(5);
digitalWrite(PING_PIN, LOW);

pinMode(PING_PIN, INPUT);
return pulseIn(PING_PIN, HIGH);
}

int durationToNote(int duration) {
duration = constrain(duration, 200, 2000);
int index = map(duration, 200, 2000, 0, sizeof(notes));
return notes[index];
}

void setup() {
MIDI.begin(1);
}

void loop() {
int echoDuration = ping_getEchoDuration();
int note = durationToNote(echoDuration);
MIDI.sendNoteOn(note, 127, 1);
enqueue(NOTE_OFF, note, millis() + 2000);
dequeueMIDIMessage();
delay(333);
}