
๐ #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 or arduino leonardo
you can ignore everything after โvoid gamemodeโ thatโs an easter egg pong game. Pong on an 128ร32 oled display is pretty cool tho.
#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;
}