Game controller for music

Game controller for music

October 24, 2019 DIY / Maker 0

🎛 #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;
}