Last active
August 29, 2015 14:06
-
-
Save goldsborough/7bd78c5cc0b99c860f13 to your computer and use it in GitHub Desktop.
Mini Midi
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
/* | |
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