Game controller for music
🎛 #diy live #midi interface stuffed with an #arduino #promicro 🎚 the oled screen tattles what i have it doing to #ableton 🔊 the left lever is turning up a drum layer made by #sampling the #netflix #sound 🎧 a toy helicopter that squirted water donated the controller 🎮 a Sharp optical distance sensor on top controlled the muffling in the beginning.
also, if you press all the buttons down, there’s a hidden pong game easter egg.
#midicontroller #midiinterface #lofibeats #triphop #futurebass #custommusic #diymusic #onlineisthenewFM
thanks @sparkfun @arduino.cc and especially @kickeraudio for providing the headphones
Code for SparkFun Pro Micro
#include <Arduino.h>
#include <U8g2lib.h>
#include <SPI.h>
#include <Wire.h>
#include <MIDIUSB.h>
U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R2);
// Woz Supposedly of Greasy Conversation - 7-30-18
// initialize everything
int space = 30;
int channel = 12;
bool game = false;
int r = 1;
int xa;
int xb;
int ya;
int yb;
int p1pad = 16;
int p2pad = 16;
int p1padchange = 16;
int p2padchange = 16;
int pspeed = 20;
int p1score = 0;
int p2score = 0;
int pballangle = 3;
int px = 16;
int py = 16;
int pwin = 14;
int pvol = 0;
#define p1control A2
#define p2control A0
#define distcontrol A3
bool distoff;
int distchange = 0;
#define distfilter 15
int mmap[3] =
{0, 0, 0};
int lmap[3] =
{0, 0, 0};
int swi;
bool b1;
bool b2;
bool b3;
bool b4;
bool tl;
bool tr;
#define NUM_AI 3
#define ANALOGUE_PIN_ORDER A2, A1, A0
#define MIDI_CC MIDI_CC_GENERAL1
// A knob or slider movement must initially exceed this value to be recognised as an input. Note that it is
// for a 7-bit (0-127) MIDI value.
#define FILTER_AMOUNT 12
// Timeout is in microseconds
#define ANALOGUE_INPUT_CHANGE_TIMEOUT 200000
// Array containing a mapping of analogue pins to channel index. This array size must match NUM_AI above.
byte analogueInputMapping[3] = {ANALOGUE_PIN_ORDER};
// Contains the current value of the analogue inputs.
byte analogueInputs[3];
// Variable to hold temporary analogue values, used for analogue filtering logic.
byte tempAnalogueInput;
// Preallocate the for loop index so we don't keep reallocating it for every program iteration.
byte i = 0;
byte digitalOffset = 0;
// Variable to hold difference between current and new analogue input values.
byte analogueDiff = 0;
// This is used as a flag to indicate that an analogue input is changing.
boolean analogueInputChanging[3];
// Time the analogue input was last moved
unsigned long analogueInputTimer[3];
void controlChange(byte thechannel, byte control, byte value) {
midiEventPacket_t event = {0x0B, 0xB0 | thechannel, control, value};
MidiUSB.sendMIDI(event);
MidiUSB.flush();
}
void pitchBendChange(byte thechannel, int value) {
byte lowValue = value & 0x7F;
byte highValue = value >> 7;
midiEventPacket_t event = {0x0E, 0xE0 | thechannel, lowValue, highValue};
MidiUSB.sendMIDI(event);
MidiUSB.flush();
}
void setup() {
delay(2000);
u8g2.begin();
pinMode(10, INPUT_PULLUP);//buttons bottom
pinMode(16, INPUT_PULLUP);
pinMode(14, INPUT_PULLUP);
pinMode(15, INPUT_PULLUP);
pinMode(4, INPUT_PULLUP);//trigger buttons
pinMode(5, INPUT_PULLUP);
pinMode(7, INPUT_PULLUP);//switches
pinMode(8, INPUT_PULLUP);
pinMode(9, INPUT_PULLUP);
}
void oled() {
u8g2.clearBuffer(); // clear the internal memory
u8g2.setFont(u8g2_font_logisoso16_tr);
u8g2.setCursor(26, 29);
if (b1) {
u8g2.drawDisc(43, 5, 5, U8G2_DRAW_UPPER_LEFT | U8G2_DRAW_LOWER_LEFT); //button indicators
u8g2.print("flange");
}
if (b2) {
u8g2.drawDisc(46, 5, 5, U8G2_DRAW_UPPER_RIGHT | U8G2_DRAW_LOWER_RIGHT);
u8g2.print("chorus");
}
if (b3) {
u8g2.drawDisc(74, 5, 5, U8G2_DRAW_UPPER_LEFT | U8G2_DRAW_LOWER_LEFT);
u8g2.print("radiate");
}
if (b4) {
u8g2.drawDisc(77, 5, 5, U8G2_DRAW_UPPER_RIGHT | U8G2_DRAW_LOWER_RIGHT);
u8g2.print("crumble");
}
if (tl) {
u8g2.drawBox(26, 0, 10, 10);
u8g2.print("slice");
}
if (tr) {
u8g2.drawBox(85, 0, 10, 10);
u8g2.print("chop");
}
int handle;
for (int lever = 0; lever < 3; lever++) { //levers
u8g2.drawLine(3 + (lever * 8), 3, 3 + (lever * 8), 29 );
if (lever == swi) {
handle = 3;
} else {
handle = 2;
}
u8g2.drawDisc(3 + (lever * 8), 31 - (lmap[lever] / 4), handle, U8G2_DRAW_ALL);
}
u8g2.drawDisc(96 + (mmap[1] / 8), 32 - (mmap[2] / 8), 3, U8G2_DRAW_ALL); //joystick indicator
u8g2.drawCircle(113, 15, 5, U8G2_DRAW_ALL);
if (!distoff) { //distance sensor indicator
u8g2.drawCircle(60, 5, r, U8G2_DRAW_ALL);
} else {
u8g2.drawLine(56, 1, 64, 9);
u8g2.drawLine(64, 1, 56, 9);
}
u8g2.sendBuffer(); // transfer internal memory to the display
}
void buttons() {
if (!digitalRead(10)) {
if (!b1) {
b1 = true;
controlChange(channel, space + 11, 127);
oled();
}
} else {
if (b1) {
b1 = false;
controlChange(channel, space + 11, 0);
oled();
}
}
if (!digitalRead(16)) {
if (!b2) {
b2 = true;
controlChange(channel, space + 12, 127);
oled();
}
} else {
if (b2) {
b2 = false;
controlChange(channel, space + 12, 0);
oled();
}
}
if (!digitalRead(14)) {
if (!b3) {
b3 = true;
controlChange(channel, space + 13, 127);
oled();
}
} else {
if (b3) {
b3 = false;
controlChange(channel, space + 13, 0);
oled();
}
}
if (!digitalRead(15)) {
if (!b4) {
b4 = true;
controlChange(channel, space + 14, 127);
oled();
}
} else {
if (b4) {
b4 = false;
controlChange(channel, space + 14, 0);
oled();
}
}
if (!digitalRead(4)) {
if (!tl) {
tl = true;
controlChange(channel, space + 10, 127);
oled();
}
} else {
if (tl) {
tl = false;
controlChange(channel, space + 10, 0);
oled();
}
}
if (!digitalRead(5)) {
if (!tr) {
tr = true;
controlChange(channel, space + 15, 127);
oled();
}
} else {
if (tr) {
tr = false;
controlChange(channel, space + 15, 0);
oled();
}
}
if (!digitalRead(10)&&!digitalRead(16)&&!digitalRead(14)&&!digitalRead(15)&&!digitalRead(4)&&!digitalRead(5)) {
gameMode();
}
}
void loop() {
if (!digitalRead(8)) {
if (swi != 0) {
swi = 0;
oled();
}
}
if (!digitalRead(9)) {
if (swi != 2) {
swi = 2;
oled();
}
}
if (digitalRead(8)&&digitalRead(9)) {
if (swi != 1) {
swi = 1;
oled();
}
}
if (!digitalRead(7)) {
if (distoff) {
distoff = false;
oled();
}
int distcheck = analogRead(A3);
if (distcheck < 150) {
distcheck = 0;
}
if ((distcheck < distchange - distfilter) || (distcheck > distchange + distfilter)) {
if (distcheck > 1000) {
distcheck = 0;
}
int dist = ((distcheck - 120) / 4.5);
if (dist > 127) {
dist = 127;
}
if (dist < 0) {
dist = 0;
}
r = dist / 2;
controlChange(channel, space, dist);
distchange = distcheck;
oled();
}
} else {
if (!distoff) {
distoff = true;
r = 0;
controlChange(channel, space, 0);
oled();
}
}
buttons();
for (i = 0; i < 3; i++)
{
// Read the analogue input pin, dividing it by 8 so the 10-bit ADC value (0-1023) is converted to a 7-bit MIDI value (0-127).
tempAnalogueInput = analogRead(analogueInputMapping[i]) / 4;
// Take the absolute value of the difference between the curent and new values
analogueDiff = abs(tempAnalogueInput - analogueInputs[i]);
// Only continue if the threshold was exceeded, or the input was already changing
if ((analogueDiff > 0 && analogueInputChanging[i] == true) || analogueDiff >= FILTER_AMOUNT)
{
// Only restart the timer if we're sure the input isn't 'between' a value
// ie. It's moved more than FILTER_AMOUNT
if (analogueInputChanging[i] == false || analogueDiff >= FILTER_AMOUNT)
{
// Reset the last time the input was moved
analogueInputTimer[i] = micros();
// The analogue input is moving
analogueInputChanging[i] = true;
}
else if (micros() - analogueInputTimer[i] > ANALOGUE_INPUT_CHANGE_TIMEOUT)
{
analogueInputChanging[i] = false;
}
// Only send data if we know the analogue input is moving
if (analogueInputChanging[i] == true)
{
// Record the new analogue value
analogueInputs[i] = tempAnalogueInput;
mmap[i] = tempAnalogueInput;
u8g2.setCursor(26, 29); //text location
if (i == 0) {
lmap[swi] = tempAnalogueInput / 2;
if (swi == 0) {
controlChange(channel, space + 1 + swi, tempAnalogueInput / 2);
u8g2.print("drums+");
}
if (swi == 1) {
controlChange(channel, space + 1 + swi, tempAnalogueInput / 2);
u8g2.print("cheapness+");
}
if (swi == 2) {
controlChange(channel, space + 20, tempAnalogueInput / 2);
u8g2.print("cheap type");
}
u8g2.sendBuffer(); // transfer internal memory to the display
}
if (i == 1) {
if (tempAnalogueInput < 139) {
u8g2.print("glitch bass");
xa = round((138 - tempAnalogueInput) * .95) - 2;
if (xa > 128) {
xa = 128;
}
if (xa < 0) {
xa = 0;
}
controlChange(channel, space + 4, xa);
}
if (tempAnalogueInput > 136) {
u8g2.print("fuzz bass");
xb = round((tempAnalogueInput - 136) * 1.11) - 2;
if (xb > 128) {
xb = 128;
}
if (xb < 0) {
xb = 0;
}
controlChange(channel, space + 5, xb);
}
u8g2.sendBuffer(); // transfer internal memory to the display
}
if (i == 2) {
if (tempAnalogueInput < 139) {
u8g2.print("glitch drum");
ya = round((138 - tempAnalogueInput) * .95) - 2;
if (ya > 128) {
ya = 128;
}
if (ya < 0) {
ya = 0;
}
controlChange(channel, space + 6, ya);
}
if (tempAnalogueInput > 136) {
u8g2.print("fuzz drum");
yb = round((tempAnalogueInput - 136) * 1.11) - 2;
if (yb > 128) {
yb = 128;
}
if (yb < 0) {
yb = 0;
}
controlChange(channel, space + 7, yb);
}
u8g2.sendBuffer(); // transfer internal memory to the display
}
oled();
}
}
}
}
void gameMode() {
p1pad = 34 - analogRead(p1control) / 34;
p1padchange = p1pad;
while ((p1padchange > p1pad - 2) && (p1padchange < p1pad + 2)) {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_6x12_t_symbols); // choose a suitable font at https://github.com/olikraus/u8g2/wiki/fntlistall
u8g2.setCursor(0, 8);
u8g2.print("w o z p o n g");
u8g2.setFont(u8g2_font_logisoso16_tr);
u8g2.setCursor(0, 26);
u8g2.print("player 1 start");
u8g2.sendBuffer();
p1pad = 34 - analogRead(p1control) / 34;
}
while (digitalRead(10)&&digitalRead(16)&&digitalRead(14)&&digitalRead(15)&&digitalRead(4)&&digitalRead(5)) {
delay(pspeed);
u8g2.clearBuffer(); // clear the internal memory
if (pballangle == 1) {
px++;
py--;
py--;
}
if (pballangle == 2) {
px++;
py--;
px++;
py--;
}
if (pballangle == 3) {
px++;
px++;
py--;
}
if (pballangle == 4) {
px++;
px++;
py++;
}
if (pballangle == 5) {
px++;
py++;
px++;
py++;
}
if (pballangle == 6) {
px++;
py++;
py++;
}
if (pballangle == 7) {
px--;
py++;
py++;
}
if (pballangle == 8) {
px--;
py++;
px--;
py++;
}
if (pballangle == 9) {
px--;
px--;
py++;
}
if (pballangle == 10) {
px--;
px--;
py--;
}
if (pballangle == 11) {
px--;
py--;
px--;
py--;
}
if (pballangle == 12) {
px--;
py--;
py--;
}
if (pballangle == 13) {
px++;
px++;
}
if (pballangle == 14) {
px--;
px--;
}
if (py < 1) {
if (pballangle == 10) {
pballangle = 9;
}
if (pballangle == 11) {
pballangle = 8;
}
if (pballangle == 12) {
pballangle = 7;
}
if (pballangle == 1) {
pballangle = 6;
}
if (pballangle == 2) {
pballangle = 5;
}
if (pballangle == 3) {
pballangle = 4;
}
}
if (py > 32) {
if (pballangle == 4) {
pballangle = 3;
}
if (pballangle == 5) {
pballangle = 2;
}
if (pballangle == 6) {
pballangle = 1;
}
if (pballangle == 7) {
pballangle = 12;
}
if (pballangle == 8) {
pballangle = 11;
}
if (pballangle == 9) {
pballangle = 10;
}
}
if (px < 4) {
pspeed--;
if (pspeed < 0) {
pspeed = 0;
}
pvol++;
if (py == p1pad - 5) {
pballangle = 1;
}
else if (py == p1pad - 4) {
pballangle = 2;
}
else if (py == p1pad - 3) {
pballangle = 2;
}
else if (py == p1pad - 2) {
pballangle = 3;
}
else if (py == p1pad - 1) {
pballangle = 3;
}
else if (py == p1pad) {
pballangle = 13;
}
else if (py == p1pad + 1) {
pballangle = 4;
}
else if (py == p1pad + 2) {
pballangle = 4;
}
else if (py == p1pad + 3) {
pballangle = 5;
}
else if (py == p1pad + 4) {
pballangle = 5;
}
else if (py == p1pad + 5) {
pballangle = 6;
}
else if ((py > p1pad + 3) || (py < p1pad - 3)) {
//p1 lost
p2score++;
if (p2score > pwin) {
p2win();
} else {
for (int line = 1; line < 10; line = line + 2) {
u8g2.drawCircle(line, py, line, U8G2_DRAW_UPPER_RIGHT);
u8g2.drawCircle(line, py, line, U8G2_DRAW_LOWER_RIGHT);
delay(20);
u8g2.sendBuffer();
}
p2pad = 34 - (analogRead(p2control) / 34);
p2padchange = p2pad;
while ((p2padchange > p2pad - 2) && (p2padchange < p2pad + 2)) {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_6x12_t_symbols); // choose a suitable font at https://github.com/olikraus/u8g2/wiki/fntlistall
u8g2.setCursor(0, 8);
u8g2.print("w o z p o n g");
u8g2.setFont(u8g2_font_logisoso16_tr);
u8g2.setCursor(0, 26);
u8g2.print("player 2 start");
pspeed = 20;
u8g2.sendBuffer();
p2pad = 34 - (analogRead(p2control) / 34);
}
if (p2pad > p2padchange) {
pballangle = 9;
}
if (p2pad < p2padchange) {
pballangle = 10;
}
py = p2pad;
px = 123;
pvol = 0;
}
}
}
if (px > 123) {
pspeed--;
if (pspeed < 0) {
pspeed = 0;
}
pvol++;
if (py == p2pad - 5) {
pballangle = 12;
}
else if (py == p2pad - 4) {
pballangle = 11;
}
else if (py == p2pad - 3) {
pballangle = 11;
}
else if (py == p2pad - 2) {
pballangle = 10;
}
else if (py == p2pad - 1) {
pballangle = 10;
}
else if (py == p2pad) {
pballangle = 14;
}
else if (py == p2pad + 1) {
pballangle = 9;
}
else if (py == p2pad + 2) {
pballangle = 9;
}
else if (py == p2pad + 3) {
pballangle = 8;
}
else if (py == p2pad + 4) {
pballangle = 8;
}
else if (py == p2pad + 5) {
pballangle = 7;
}
else if ((py > p2pad + 3) || (py < p2pad - 3)) {
//p2 lost
p1score++;
if (p1score > pwin) {
p1win();
} else {
for (int line = 1; line < 10; line = line + 2) {
u8g2.drawCircle(127 - line, py, line, U8G2_DRAW_UPPER_LEFT);
u8g2.drawCircle(127 - line, py, line, U8G2_DRAW_LOWER_LEFT);
delay(20);
u8g2.sendBuffer();
}
p1pad = 34 - analogRead(p1control) / 34;
p1padchange = p1pad;
while ((p1padchange > p1pad - 2) && (p1padchange < p1pad + 2)) {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_6x12_t_symbols); // choose a suitable font at https://github.com/olikraus/u8g2/wiki/fntlistall
u8g2.setCursor(0, 8);
u8g2.print("w o z p o n g");
u8g2.setFont(u8g2_font_logisoso16_tr);
u8g2.setCursor(0, 26);
u8g2.print("player 1 start");
pspeed = 20;
u8g2.sendBuffer();
p1pad = 34 - analogRead(p1control) / 34;
}
if (p1pad > p1padchange) {
pballangle = 4;
}
if (p1pad < p1padchange) {
pballangle = 3;
}
py = p1pad;
px = 4;
pvol = 0;
}
}
}
if (!digitalRead(7)){
p1pad = analogRead(distcontrol) / 27 +3 ;
p2pad = analogRead(distcontrol) / 27 +3 ;
}else{
p1pad = 34 - analogRead(p1control) / 34;
p2pad = 34 - analogRead(p2control) / 34;
}
if (!digitalRead(10)&&!digitalRead(16)&&!digitalRead(14)&&!digitalRead(15)&&!digitalRead(4)&&!digitalRead(5)) {
break;
}
u8g2.drawLine(0, p1pad - 2, 0, p1pad + 2 );
u8g2.drawLine(1, p1pad - 3, 1, p1pad + 3 );
u8g2.drawLine(2, p1pad - 2, 2, p1pad + 2 );
u8g2.drawLine(125, p2pad - 2, 125, p2pad + 2 );
u8g2.drawLine(126, p2pad - 3, 126, p2pad + 3 );
u8g2.drawLine(127, p2pad - 2, 127, p2pad + 2 );
u8g2.drawDisc(px, py, 2, U8G2_DRAW_ALL);
u8g2.setFont(u8g2_font_6x12_t_symbols);
if (p1score < 10) {
u8g2.setCursor(56, 10);
} else {
u8g2.setCursor(49, 10);
}
u8g2.print(p1score);
u8g2.setCursor(66, 10);
u8g2.print(p2score);
u8g2.setCursor(66, 30);
u8g2.print(pvol);
for (int line = 1; line < 128; line = line + 2) {
u8g2.drawLine(63, line, 63, line);
}
u8g2.sendBuffer();
}
}
void p1win() {
for (int line = 1; line < 100; line = line + 2) {
u8g2.drawCircle(127 - line, py, line, U8G2_DRAW_UPPER_LEFT);
u8g2.drawCircle(127 - line, py, line, U8G2_DRAW_LOWER_LEFT);
delay(20);
u8g2.sendBuffer();
}
p2pad = 34 - analogRead(p2control) / 34;
p2padchange = p2pad;
while ((p2padchange > p2pad - 2) && (p2padchange < p2pad + 2)) {
u8g2.clearBuffer(); // clear the internal memory
u8g2.setFont(u8g2_font_logisoso16_tr); // choose a suitable font at https://github.com/olikraus/u8g2/wiki/fntlistall
u8g2.setCursor(0, 16);
u8g2.print("player 1 WINS");
u8g2.setFont(u8g2_font_6x12_t_symbols); // choose a suitable font at https://github.com/olikraus/u8g2/wiki/fntlistall
u8g2.setCursor(0, 30);
u8g2.print("now player 2 starts");
pspeed = 20;
u8g2.sendBuffer();
p2pad = 34 - analogRead(p2control) / 34;
}
if (p2pad > p2padchange) {
pballangle = 9;
}
if (p2pad < p2padchange) {
pballangle = 10;
}
py = p2pad;
px = 123;
p1score = 0;
p2score = 0;
pvol = 0;
}
void p2win() {
for (int line = 1; line < 100; line = line + 2) {
u8g2.drawCircle(line, py, line, U8G2_DRAW_UPPER_RIGHT);
u8g2.drawCircle(line, py, line, U8G2_DRAW_LOWER_RIGHT);
delay(20);
u8g2.sendBuffer();
}
p1pad = 34 - analogRead(p1control) / 34;
p1padchange = p1pad;
while ((p1padchange > p1pad - 2) && (p1padchange < p1pad + 2)) {
u8g2.clearBuffer(); // clear the internal memory
u8g2.setFont(u8g2_font_logisoso16_tr); // choose a suitable font at https://github.com/olikraus/u8g2/wiki/fntlistall
u8g2.setCursor(0, 16);
u8g2.print("player 2 WINS");
u8g2.setFont(u8g2_font_6x12_t_symbols); // choose a suitable font at https://github.com/olikraus/u8g2/wiki/fntlistall
u8g2.setCursor(0, 30);
u8g2.print("now player 1 starts");
pspeed = 20;
u8g2.sendBuffer();
p1pad = 34 - analogRead(p1control) / 34;
}
if (p1pad > p1padchange) {
pballangle = 4;
}
if (p1pad < p1padchange) {
pballangle = 3;
}
py = p1pad;
px = 4;
p1score = 0;
p2score = 0;
pvol = 0;
}