Skip to content

Instantly share code, notes, and snippets.

@oshoham
Created February 14, 2018 08:21
Show Gist options
  • Save oshoham/0643d00a4f3f81d2c436a0fe31e36446 to your computer and use it in GitHub Desktop.
Save oshoham/0643d00a4f3f81d2c436a0fe31e36446 to your computer and use it in GitHub Desktop.
Tangible Interaction Workshop MIDI Controller
#include <Adafruit_LIS3DH.h>
#define USE_ACCELEROMETER true
const int sliderPins[7] = { A1, A2, A3, A6, A7, A8, A9 };
const int sliderThreshold = 20;
const int sliderNoiseThreshold = 30;
const int rotarySwitchPins[4] = { 5, 4, 3, 2 };
const int toggleSwitchPins[2] = { 7, 8 };
const int majorScale[7] = { 0, 2, 4, 5, 7, 9, 11 };
const int minorScale[7] = { 0, 2, 3, 5, 7, 8, 10 };
const int harmonicMinorScale[7] = { 0, 2, 3, 5, 7, 9, 10 };
const int wholeToneScale[7] = { 0, 2, 4, 6, 8, 10 };
const int octaves[3] = { 72, 60, 84 };
const float accelerometerNoiseThreshold = 2.0;
bool accelerometerEnabled = false;
float accelerometerYInitialAverage = 0.0;
int sliderStates[7] = { 0, 0, 0, 0, 0, 0, 0 };
int rotarySwitchState = 0;
int toggleSwitchState = 0;
float accelerometerYState = 0.0;
bool pitchBendState = false;
Adafruit_LIS3DH lis = Adafruit_LIS3DH();
void setup() {
// initialize serial communications at 9600 bps:
Serial.begin(9600);
Serial1.begin(31250);
if (! lis.begin(0x18)) { // change this to 0x19 for alternative i2c address
Serial.println("Couldn't init LS3DH Accelerometer");
} else {
accelerometerEnabled = true;
Serial.println("LIS3DH found!");
lis.setRange(LIS3DH_RANGE_4_G); // 2, 4, 8 or 16 G!
Serial.print("Range = "); Serial.print(2 << lis.getRange());
Serial.println("G");
Serial.println("Calibrating accelerometer...");
int t = 0;
while (millis() < 5000) {
t++;
sensors_event_t event;
lis.getEvent(&event);
accelerometerYInitialAverage += event.acceleration.y;
}
accelerometerYInitialAverage /= t;
Serial.println("Done!");
}
for (int i = 0; i < 4; i++) {
pinMode(rotarySwitchPins[i], INPUT);
}
for (int j = 0; j < 2; j++) {
pinMode(toggleSwitchPins[j], INPUT);
}
}
void loop() {
// debug();
// set the scale
int rotarySwitchPosition = 0;
for (int i = 0; i < 4; i++) {
int rotarySwitchValue = digitalRead(rotarySwitchPins[i]);
if (rotarySwitchValue == HIGH) {
rotarySwitchPosition = i;
break;
}
}
int scale[7];
if (rotarySwitchPosition == 0) {
memcpy(scale, majorScale, sizeof(int) * 7);
} else if (rotarySwitchPosition == 1) {
memcpy(scale, minorScale, sizeof(int) * 7);
} else if (rotarySwitchPosition == 2) {
memcpy(scale, harmonicMinorScale, sizeof(int) * 7);
} else if (rotarySwitchPosition == 3) {
memcpy(scale, wholeToneScale, sizeof(int) * 7);
}
// set the octave
int toggleSwitchPosition = 0;
for (int j = 0; j < 2; j++) {
int toggleSwitchValue = digitalRead(toggleSwitchPins[j]);
if (toggleSwitchValue == HIGH) {
toggleSwitchPosition = j + 1;
break;
}
}
int octave = octaves[toggleSwitchPosition];
// handle changing notes if we changed scales or octaves
if (rotarySwitchState != rotarySwitchPosition || toggleSwitchState != toggleSwitchPosition) {
int oldOctave = octaves[toggleSwitchState];
int oldScale[7];
if (rotarySwitchState == 0) {
memcpy(oldScale, majorScale, sizeof(int) * 7);
} else if (rotarySwitchState == 1) {
memcpy(oldScale, minorScale, sizeof(int) * 7);
} else if (rotarySwitchState == 2) {
memcpy(oldScale, harmonicMinorScale, sizeof(int) * 7);
} else if (rotarySwitchState == 3) {
memcpy(oldScale, wholeToneScale, sizeof(int) * 7);
}
for (int l = 0; l < 7; l++) {
if (sliderStates[l] > sliderThreshold) {
int oldNote = oldOctave + oldScale[l];
int note = octave + scale[l];
if (oldNote != note) {
midiCommand(0x80, oldNote, 0);
int velocity = map(sliderStates[l], 0, 1023, 0, 127);
midiCommand(0x90, note, velocity);
}
}
}
rotarySwitchState = rotarySwitchPosition;
toggleSwitchState = toggleSwitchPosition;
}
// play the notes that are currently on
for (int k = 0; k < 7; k++) {
int sliderValue = analogRead(sliderPins[k]);
if (abs(sliderValue - sliderStates[k]) > sliderNoiseThreshold) {
sliderStates[k] = sliderValue;
if (sliderValue > sliderThreshold) {
int velocity = map(sliderValue, 0, 1023, 0, 127);
midiCommand(0x90, octave + scale[k], velocity);
} else {
midiCommand(0x80, octave + scale[k], 0);
}
} else if(sliderStates[k] > sliderThreshold && sliderValue < sliderThreshold) {
midiCommand(0x80, octave + scale[k], 0);
}
}
bool noteIsPlaying = false;
for (int m = 0; m < 7; m++) {
if (sliderStates[m] > sliderThreshold) {
noteIsPlaying = true;
break;
}
}
if (USE_ACCELEROMETER && accelerometerEnabled && noteIsPlaying) {
sensors_event_t event;
lis.getEvent(&event);
float yAcceleration = -1.0 * event.acceleration.y;
// range of event.acceleration.y is approx. -10 to 10
if (abs(yAcceleration - accelerometerYState) > accelerometerNoiseThreshold) {
if (abs(abs(yAcceleration) - abs(accelerometerYInitialAverage)) > 2.0) {
int mappedYAcceleration = map(yAcceleration, -10.0, 10.0, -8192, 8192);
unsigned char lsb = mappedYAcceleration & 0x7F; // Low 7 bits
unsigned char msb = (mappedYAcceleration >> 7) & 0x7F; // High 7 bits
bitWrite(lsb, 7, 0); // lsb should always have 0 in it's highest bit
midiCommand(0xE0, lsb, msb);
} else {
midiCommand(0xE0, 64, 0); // no pitch bend
}
accelerometerYState = yAcceleration;
}
}
}
void midiCommand(byte cmd, byte data1, byte data2) {
Serial1.write(cmd); // command byte (should be > 127)
Serial1.write(data1); // data byte 1 (should be < 128)
Serial1.write(data2); // data byte 2 (should be < 128)
}
void debug() {
// for (int i = 0; i < 7; i++) {
// int sensorValue = analogRead(sliderPins[i]);
// Serial.print("Slider ");
// Serial.print(i);
// Serial.print(": ");
// Serial.print(sensorValue);
// Serial.print(", ");
// }
// for (int j = 0; j < 4; j++) {
// int rotarySwitchValue = digitalRead(rotarySwitchPins[j]);
// Serial.print("Rotary Switch Pos ");
// Serial.print(j);
// Serial.print(": ");
// Serial.print(rotarySwitchValue);
// Serial.print(", ");
// }
// for (int k = 0; k < 2; k++) {
// int toggleSwitchValue = digitalRead(toggleSwitchPins[k]);
// Serial.print("Toggle Switch Pos ");
// Serial.print(k);
// Serial.print(": ");
// Serial.print(toggleSwitchValue);
// Serial.print(", ");
// }
// sensors_event_t event;
// lis.getEvent(&event);
//
// Serial.print("X: "); Serial.print(event.acceleration.x);
// Serial.print(" \tY: "); Serial.print(event.acceleration.y);
// Serial.print(" \tZ: "); Serial.print(event.acceleration.z);
// Serial.println("");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment