That makes me happy to hear!
Would you be able to be a bit more specific in explaining how to do this?
I have built a similar project using only two handles on a bench. This is the code. Modified from another project.
/*
Exploratorium Tinkering Snack version of Musical / Singing Bench.
Uses Arduino USB and a slightly modified SparkFun Musical Insturment Shield, see the
website for the schematic and build instructions.
A simple resistive voltage divider sensor circuit is used for input. The harder
a person presses, or the more surface is in contact with the touch pads, or the
moister a person's hands, the lower the touch resistence will be. Low resistences are
mapped to high notes, so the harder a person presses, the higher the note. A light touch
gets the low notes.
High sensor values are open circuit; touch resistence is the pull-down in the sensor circuit.
This is so that the fixed pull-up resistor in the sensor circuit protects VCC from the
cold, cruel outside world (GND is less vulnerable). It's a bit counter-intuitive at first,
but much more robust.
This is about as stripped down as it gets! It arpeggiates the chromatic scale between limits
set by the macros LOWEST_NOTE and HIGHEST_NOTE, with a fixed attack velocity
and a single instrument voice. The lowest and highest notes are not likely to be heard,
the sensor range will be in practice restricted and stay near the low half of the range,
but the mapping is defined for the full theoretical range to avoid burdensome error checking.
Chromatics are not the most pleasing of scales, but it uses the simplest possible method
of translating sensor values into note values: the map() function. It sounds like a horror movie
soundtrack, but that's kind of fun!
Two simple anti-noise strategies are in use. First, sensor values are not accepted unless they
differ from the last accepted value by a minimum amount: sensor hysteresis. Second, there is
a dead zone at the open-circuit end of the sensor range: the noise floor. Without these, it
would be pretty hard to have fun with this critter, it's a complication worth putting up with.
The main tweak opportunities are collected into the macros just below. Have fun with them!
Extra credit: can you spot why there is a single "glitch" note when this sketch starts?
*/
// Libraries
#include <SoftwareSerial.h> // needed for MIDI communications with the MI shield
// These are the most tweak-able macros, the ones that really define how it "plays"
#define SIXTEENTH_NOTE_MS 100 // Approx. loop rate in millis, all note durations will be integer multiples of this. Tweak to speed up or slow down the arpeggio.
#define SENSOR_HYSTERESIS 1 // ignore incremental changes smaller than this value... needed to kill noise, but also affects how busy and dense the arpeggio is
#define LOWEST_NOTE 70 // 48 = C3 in MIDI; the piano keyboard ranges from A0 = 21 through C8 = 108, the lowest octaves can be pretty muddy sounding
#define HIGHEST_NOTE 150 // 96 = C7. A smaller range will result in a less busy feel, and may allow more repeated strikes of the same note. You probably won't hear half of this range with human touch, but short the touch pads with metal and you will
// these should need less tweaking, but go ahead and see what they do
#define NOISE_FLOOR 1000 // out of 1023, higher than this is considered open circuit.
#define VELOCITY 100 // note strike "force", max 127
// From here on down, tweaking is probably not a great idea...
// pin assignments
#define RX_PIN 2 // Soft serial Rx; M.I shield TxD
#define TX_PIN 3 // shield RxD
#define LED_PIN 13 // Arduino native LED
#define SENSOR_PIN A0 // also known as P14
// Standard MIDI, ADDRESS == CH1 which is all we propose to use
#define MIDI_BAUD 31250
#define CONT_CHANGE 176 // controller change message, Channel 1
#define LIGHT_ON 177 // MidiCC Channel for controlling light, Channel 2
#define modWheel 1 // Modulation Wheel, MidiCC
#define intensity 0 // Light intensity
#define NOTE_ON 144 // Note On, Channel 1
#define ALL_OFF 123 // Using "all notes off" rather than "note off" for single voice MIDI is a common tactic; MIDI devices are notorious for orphaning notes
// Global variables and objects
SoftwareSerial MIDIserial(RX_PIN, TX_PIN); // channel for MIDI comms with the MI shield
unsigned int touchSensor; // holds the most recent accepted sensor value.
unsigned int sensorBuffer; // holds a provisional sensor value while we look at it
unsigned int note; // holds the calculated MIDI note value
int chordNote[] = {48, 52, 55, 60, 64, 67, 72, 76, 79, 84, 88, 91, 96, 100, 103, 108};
void setup() {
Serial.begin(9600); // USB is for debugging and tuning info, make sure your serial monitor is set to match this baud rate
delay(1000);
//Start soft serial channel for MIDI, and send one-time only messages
MIDIserial.begin(MIDI_BAUD);
}
void loop() {
sensorBuffer = analogRead(SENSOR_PIN); // get new sensor value to examine
//MidiCC function for controlling light
if(abs(sensorBuffer - intensity > 2)){ // Only accept a new sensor value if it differs from the last accepted value by the minimum amount (2)
intensity == map(sensorBuffer, 500, 1023, 127, 0);
talkMIDI(LIGHT_ON , modWheel, intensity); // Update light intensity
}
if (abs(sensorBuffer-touchSensor) >= SENSOR_HYSTERESIS) { // only accept a new sensor value if it differs from the last accepted value by the minimum amount
touchSensor = sensorBuffer; // accept the new sensor value, but don't play a note yet...
// if (touchSensor <= NOISE_FLOOR){ // only play a note if the accepted sensor value is below the noise floor
// we've passed both the anti-noise tests; time to calculate and play a new note
switch (touchSensor){
case 500 ... 530: note = chordNote[15]; break;
case 531 ... 560: note = chordNote[14]; break;
case 561 ... 590: note = chordNote[13]; break;
case 591 ... 620: note = chordNote[12]; break;
case 621 ... 650: note = chordNote[11]; break;
case 651 ... 680: note = chordNote[10]; break;
case 681 ... 710: note = chordNote[9]; break;
case 711 ... 740: note = chordNote[8]; break;
case 741 ... 770: note = chordNote[7]; break;
case 771 ... 800: note = chordNote[6]; break;
case 801 ... 830: note = chordNote[5]; break;
case 831 ... 860: note = chordNote[4]; break;
case 861 ... 890: note = chordNote[3]; break;
case 891 ... 920: note = chordNote[2]; break;
case 921 ... 950: note = chordNote[1]; break;
case 951 ... 1000: note = chordNote[0]; break;
}
// note = map(touchSensor, 0, 1023, HIGHEST_NOTE, LOWEST_NOTE); // high sensor values (high resistence, light touch) map to low notes and V.V.
talkMIDI(CONT_CHANGE , ALL_OFF, 0); // turn off previous note(s); comment this out if you like the notes to ring over each other
talkMIDI(NOTE_ON , note, VELOCITY); // play new note!
Serial.print(touchSensor); // report out values in case anyone is monitoring
Serial.print(' ');
Serial.println(note);
// }
}
delay (SIXTEENTH_NOTE_MS); // tempo: do nothing for sixteenth note duration, the rest of the loop will execute so fast we'll never notice it
}
//Sends all the MIDI messages we need for this sketch.
void talkMIDI(byte cmd, byte data1, byte data2) {
MIDIserial.write(cmd);
MIDIserial.write(data1);
MIDIserial.write(data2);
}