Created
February 14, 2018 08:21
-
-
Save oshoham/0643d00a4f3f81d2c436a0fe31e36446 to your computer and use it in GitHub Desktop.
Tangible Interaction Workshop MIDI Controller
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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