Skip to content

Instantly share code, notes, and snippets.

@goldsborough
Last active August 29, 2015 14:06
Show Gist options
  • Save goldsborough/7bd78c5cc0b99c860f13 to your computer and use it in GitHub Desktop.
Save goldsborough/7bd78c5cc0b99c860f13 to your computer and use it in GitHub Desktop.
Mini Midi
/*
The MIT License (MIT)
Copyright (c) 2014 Peter Goldsborough
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <MIDI.h>
#define DEBUG true
//SHIFT REGISTERS A
const int latchPin_a = 9; //Latch pin of first shift register
const int clockPin_a = 8; //Clock pin of first shift register
const int dataPin_a = 7; //Data pin of first shift register
//SHIFT REGISTERS B
const int latchPin_b = 13; //Latch pin of second shift register
const int clockPin_b = 12; //Clock pin of second shift register
const int dataPin_b = 11; //Data pin of second shift register
// SHIFT REGISTER BUTTON
const int shift_reg_button = 21; // Button to control whether the LED bars are on or off
boolean shift_reg_state = false;
//BUTTONS A
const int fx_a = 1; //fx button left side
const int load_a = 4; //load song on left side from browser
const int play_a = 5; //play left side
// LEDS A
const int fx_a_led = 27; //it's led
const int play_a_led = 0; //it's led
//BUTTONS B
const int fx_b = 24; //fx button right side
const int load_b = 23; //load song on right side from browser
const int play_b = 22; //play right side
// LEDS B
const int fx_b_led = 26; //it's led
const int play_b_led = 25; //it's led
//ENCODER
const int encoderPin1 = 18; //Pin one of the encoder
const int encoderPin2 = 19; //Pin two of the encoder
// volatile means it can change within a loop, special type for ISR (interupt service routine)
volatile int lastEncoded = 0; //last value
volatile long encoderValue = 0; //sum of values
long lastencoderValue = 0;
int last_val = 0;
int lastMSB = 0; //Most Significant Bit -> 100000, the first 1 is meant
int lastLSB = 0; //Least Significant Bit -> the right most 0 is meant
// MUX
const int mux_0 = 17; //s0
const int mux_1 = 16; //s1
const int mux_2 = 15; //s2
int s0 = 0; // value of s0
int s1 = 0; // value of s1
int s2 = 0; // value of s2
const int mux_pin = 7; // multiplexer analog output pin
int mux_bin [] = {0b000, 0b001, 0b010, 0b011, 0b100, 0b101, 0b110, 0b111}; //list of binary values
// OTHER POTENTIOMETERS
const int slide_pot = 6; //Slide pot analog pin
/* The below pot_vals are stored at one point in the loop and later on compared with a
// second reading, if the values in that very vey short period of time have changed (basically
// if a potentiometer has turned), a signal is sent and these values are reset to those, thus
// readings are a lot more efficient because signals are only sent when a change has actually
// occured, opposed to constant sending of signals!
// Check out my blog at http://goo.gl/0yaVBo for more info on that.
*/
int pot_vals[9] = {0}; //9th value is for the slide pot
int pot_notes[9] = {60,61,62,63,64,65,66,67,68};
int bs[6] = {fx_a,play_a,fx_b,play_b,load_a,load_b}; //all the buttons
int notes[6] = {50,51,52,53,54,55};
int leds[4] = {fx_a_led,play_a_led,fx_b_led,play_b_led};
// SHIFT REGISTER PATTERNS + VARIABLES FOR FUNCTION
byte stages [] = {
0b00000000,0b11111111, // on off
0b00000000,0b10000000,0b01000000,0b00100000,0b00010000,0b00001000,0b00000100,0b00000010,0b00000001, // left to right 1 by 1
0b00000000,0b00000001,0b00000010,0b00000100,0b00001000,0b00010000,0b00100000,0b01000000,0b10000000, // right to left 1 by 1
0b00000000,0b00011000,0b00100100,0b01000010,0b10000001, // streaming out from middle
0b00000000,0b10000001,0b01000010,0b00100100,0b00011000, // streaming in from outside
0b00000000,0b00011000,0b00111100,0b01111110,0b11111111, // streaming out from middle, leaving previous ones on
0b00000000,0b11111111,0b01111110,0b00111100,0b00011000, // streaming in from outside, leaving previous ones on
0b00000000,0b11000000,0b01100000,0b00110000,0b00011000,0b00001100,0b00000110,0b00000011, // left to right 2 by 2
0b00000000,0b00000011,0b00000110,0b00001100,0b00011000,0b00110000,0b01100000,0b11000000, // right to left 2 by 2
0b00000000,0b11110000,0b00001111, // left to right 4 by 4
0b00000000,0b00001111,0b11110000, // right to left 4 by 4
0b00000000,0b00011000,0b00100100,0b01011010,0b10100101, // streaming out from middle, leaving previous ones blinking
0b00000000,0b10000001,0b01000010,0b10100101,0b01011010, // streaming in from outside, leaving previous ones blinking
0b00000000,0b10000000,0b01000010,0b00100100,0b00010000,0b00000001,0b00000010,0b00000100,0b00001000, // right to middle, left to middle
};
int patterns [14] = {2,9,9,5,5,5,5,8,8,3,3,5,5,9}; //how many stages each pattern has, I keep the number in the brackets just to know how many are in there
int intervs [7] = {10,25,50,75,100,150,250};
int reps [5] = {2,5,10,15,20};
int r_p; //random pattern
int r_i; //random interval
int r_r; //random repitition number
int index; //index from which the pattern starts
int interv; // pattern speed
int rep; // number of pattern repetitions
int num_stages; //how many stages it is, so we know how much to subtract if repitions are left over, if you don't care for clarity, this is basically tot_ind - index
int tot_ind; //the last index of the pattern
// Debouncing
long t; //for debouncing the buttons without delay or Bounce library
long last_t[9] = {millis()}; //initialize a list of 8 last_ts each at current millis() // 4 toggle buttons, 2 normal buttons, 1 shift_reg_button, 1 for pattern
// Channel 1: Pushbuttons
// Channel 2: Potentiometers
// ----------------------- SETUP ------------------------------- \\
void setup() {
// Shift Registers
pinMode(latchPin_a, OUTPUT);
pinMode(dataPin_a, OUTPUT);
pinMode(clockPin_a, OUTPUT);
pinMode(latchPin_b, OUTPUT);
pinMode(dataPin_b, OUTPUT);
pinMode(clockPin_b, OUTPUT);
pinMode(shift_reg_button,INPUT);
//Encoder
pinMode(encoderPin1, INPUT);
pinMode(encoderPin2, INPUT);
digitalWrite(encoderPin1, HIGH); //turn pullup resistor on
digitalWrite(encoderPin2, HIGH); //turn pullup resistor on
//call updateEncoder() when any high/low changed seen
//on interrupt 0 (pin 2), or interrupt 1 (pin 3)
attachInterrupt(6, updateEncoder, CHANGE);
attachInterrupt(7, updateEncoder, CHANGE);
//Mux
pinMode(mux_0, OUTPUT); // s0
pinMode(mux_1, OUTPUT); // s1
pinMode(mux_2, OUTPUT); // s2
//Buttons
for (int i = 0; i < 7; i++) pinMode(bs[i],INPUT); //deck buttons
//Leds
for (int i = 0; i < 4; i++) pinMode(leds[i],OUTPUT);
if (SER) Serial.begin(57600); //set serial if wanted
randomSeed(analogRead(0)); //seed random
shift(0b00000000);
get_patt();
}
void loop() {
t = millis(); // get elapsed time
check_pots();
check_buttons();
if (shift_reg_state) shift_reg();
}
void check_buttons() //check buttons for HIGH signal
{
for (int i = 0; i < 6; i++) //for every fx and play button
{
if (digitalRead(bs[i]) == HIGH && t >= last_t[i] + 250)
{
usbMIDI.sendNoteOn(notes[i],100,1);
if (i < 4)
{
if (digitalRead(leds[i]) == HIGH) digitalWrite(leds[i],LOW);
else digitalWrite(leds[i],HIGH);
}
last_t[i] = t;
}
}
if (digitalRead(shift_reg_button) && t >= last_t[6] + 200) //if shift_reg_button is HIGH
{
if (shift_reg_state == false) shift_reg_state = true; //if previous state is false, switch to false, this then triggers the if clause in the led_bars() function
else
{
shift_reg_state = false; //if previous state was on, so true, switch to false
shift(0b00000000); //make all shift_reg pins go low here because I don't want this to happen everytime during the loop
}
last_t[6] = t;
}
}
void shift(byte b)
{
digitalWrite(latchPin_a, LOW);
shiftOut(dataPin_a,clockPin_a,MSBFIRST,b);
digitalWrite(latchPin_a, HIGH);
digitalWrite(latchPin_b, LOW);
shiftOut(dataPin_b,clockPin_b,MSBFIRST,b);
digitalWrite(latchPin_b, HIGH);
}
void get_patt()
{
r_p = random() % 14; //pattern
r_i = random() % 7; //interval
r_r = random() % 5; //repetitions
interv = intervs[r_i];
rep = reps[r_r];
num_stages = patterns [r_p];
tot_ind = 0;
index = 0;
for (int i = 0; i < r_p; i++) tot_ind += patterns[i];//add all indexes until the last index of the pattern before
index = tot_ind; //since tot_ind stores the index + 1, this is exactly the first index of the pattern
tot_ind += num_stages; //then add the total stages of this pattern to get the last pattern, now we have a beginning and an end
if (DEBUG)
{
Serial.print("NEW: ");
Serial.println(r_p);
Serial.print("Interval: ");
Serial.println(interv);
Serial.print("Repetitions: ");
Serial.println(rep);
Serial.print("Number of stages: ");
Serial.println(num_stages);
Serial.print("First index: ");
Serial.println(index);
Serial.print("Last index: ");
Serial.println(tot_ind-1);
}
}
void shift_reg()
{
shift(stages[index]);
if (t >= (last_t[7] + interv))
{
digitalWrite(latchPin_a, LOW);
shiftOut(dataPin_a,clockPin_a,MSBFIRST,stages[index]);
digitalWrite(latchPin_a, HIGH);
digitalWrite(latchPin_b, LOW);
shiftOut(dataPin_b,clockPin_b,MSBFIRST,stages[index]);
digitalWrite(latchPin_b, HIGH);
if (index == tot_ind-1) //if the pattern is over
{
if (rep == 0) get_patt(); //if no more repetitions, get new pattern
else//if repetitions left, go back to first stage
{
index -= num_stages;
rep--;
}
}
index++;
last_t[7] = t; //next time the last millis() is the current millis()
}
}
void updateEncoder(){
int MSB = digitalRead(encoderPin1); //MSB = most significant bit
int LSB = digitalRead(encoderPin2); //LSB = least significant bit
int encoded = (MSB << 1) |LSB; //converting the 2 pin value to single number
int sum = (lastEncoded << 2) | encoded; //adding it to the previous encoded value
if(sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) encoderValue ++;
if(sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) encoderValue --;
if(t >= (last_t[8] + 20))
{
if (DEBUG)
{
Serial.print("encoder: ");
Serial.println(encoderValue/4);
}
if (encoderValue > last_val) usbMIDI.sendControlChange(75,70,1);
else usbMIDI.sendControlChange(75,60,1);
last_val = encoderValue;
last_t[8] = t;
}
lastEncoded = encoded; //store this value for next time
}
void check_pots()
{
int val; //analogRead value
//MULTIPLEXER
for (int i = 0; i < 8; i++) { //loop through each channel, checking for a signal
int row = mux_bin[i]; //channel 5 = 5th element in the bin list -> 101 etc.
s0 = bitRead(row,0); //bitRead() -> parameter 1 = binary sequence, parameter 2 = which bit to read, starting from the right most bit
s1 = bitRead(row,1); //channel 7 = 111, 1 = 2nd bit (starting at 0, 0 = 1st element)
s2 = bitRead(row,2); // third bit
digitalWrite(mux_0, s0); // send the bits to the digital inputs
digitalWrite(mux_1, s1);
digitalWrite(mux_2, s2);
val = analogRead(mux_pin)/8;
if (i == 0 || i == 7) val = 127 - val;
if (val != pot_vals[i] && val != pot_vals[i] + 1 && val != pot_vals[i] -1)
{
if (DEBUG)
{
Serial.print("Channel: ");
Serial.print(i+1);
Serial.print(" ==== ");
Serial.println(val); // after sending the binary sequence, the mux determines which channel to read from and sends it to this analog input
}
usbMIDI.sendControlChange(pot_notes[i],val,1);
pot_vals[i] = val;
}
}
//SLIDE POT
val = analogRead(slide_pot)/8;
if (val != pot_vals[8])
{
if (DEBUG)
{
Serial.print("Slide pot ");
Serial.print(" ==== ");
Serial.println(val); // after sending the binary sequence, the mux determines which channel to read from and sends it to this analog input
}
usbMIDI.sendControlChange(pot_notes[8],val,1);
pot_vals[8] = val;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment