Hardware/Software Development

Hardware Block Diagram




Sequencer Arduino (C/C++) Code 

Main loop

This section of code outlines the main sequence of operation for the sequencer. First, a for loop is utilized to govern which step the sequencer is operating in. Within this loop, first an address is made using the assignByteToPins() function, this sends a 4 bit binary address to ensure that the input of each analog multiplexer corresponds to the value of k (the indexing variable of the for loop). Next, an array called "note[]' is assigned a value at position k based on the analog voltage present on pin A0, this is done in a similar fashion for the velocity[] array. Once a value is stored in each array, it is then sent to the computer through USB using the usbMIDI class and the sendNoteOn(note, velocity, channel) function. Note length and tempo are then determined using either the delay() function or the lengthWait() function to stall the operation of the program for a period of milliseconds.

void loop(){
  for (int k=0;k<16;k++){
    assignByteToPins(k);
    note[k] = getValue16a(k, A0);
    velocity[k] = 99; //random(0, 2);
    usbMIDI.sendNoteOn(note[k], velocity[k], 1); 
    delay(analogRead(A3));
    usbMIDI.sendNoteOff(note[k], velocity[k], 1);
    lengthWait(A2);
  }
}


Addressing the Multiplexers 

 During operation of the sequencer, it is imperative that the desired note is played at any given time. This is accomplished by making certain that each potentiometer (the input device used to vary a note) is read at the precise time that particular note is to be played. A device known as a multiplexer is used to do this (more information on the multiplexers can be seen in the hardware section). The inputs are synced to the flow of the program by sending a 4 bit binary address to the multiplexers using the assignByteToPins function. This function takes an integer and based on its value (as long as it is between 0 and 15) will assign the corresponding binary value to the state of digital I/O pins 4 through 7. This is done by a simple if, if else ladder. While this can be accomplished more elegantly through the use of a for loop and some modulus division, it was found that writing all four pins simultaneously ensures that the desired address is sent to the multiplexer rather than a transient binary value. If a loop based function was used for instance, as the function re assigned states to each of the pins, it would change their values sequentially, and therefore effectively send multiple address signals before settling on a final state.  In the case of switching between binary 7 and binary 8, the sequence in which the pins change state would be as follows; 0111 --> 1110 --> 1100 --> 1000.


void assignByteToPins(int pos){ 
  if (pos == 0){
    digitalWrite(4, LOW);
    digitalWrite(5, LOW);
    digitalWrite(6, LOW);
    digitalWrite(7, LOW);
  }
  else if (pos == 1){
    digitalWrite(4, HIGH);
    digitalWrite(5, LOW);
    digitalWrite(6, LOW);
    digitalWrite(7, LOW);
  }
  else if (pos == 2){
    digitalWrite(4, LOW);
    digitalWrite(5, HIGH);
    digitalWrite(6, LOW);
    digitalWrite(7, LOW);
  }

.....
}

Current Testbed for code
#include <LiquidCrystalFast.h>
int soloSeqNote[16];
LiquidCrystalFast lcd(12, 10, 11, 5, 4, 3, 2);

void setup(){
  lcd.begin(16, 2);
  for(int pin=14;pin<=17;pin++){
    pinMode(pin, OUTPUT);
  }
  bootScreen();
}

void loop(){
  if(digitalRead(21) == LOW){
    programmingMode();
  }
  else if(digitalRead(21) == HIGH){
    for(int i=0;i<16;i++){
      playFourTracks(i, 99, 1);
    }
  }
}

void lengthWait(char analogPin){
  static int wait;
  wait = analogRead(analogPin);
  delay(wait);
}
 
void bootScreen(){
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Yo, Wuddup?!");
  lcd.setCursor(0,1);
  lcd.print("NACCI MIDI 16SEQ");
  delay(2000);
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("DREXEL '12");
  lcd.setCursor(0,1);
  lcd.print("Firmware V1.3");
  delay(2000);
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("MAKE SOME MUSIC!");
  delay(2000);

}
 
int getValue16a(int pos, char analogPin){ //This function takes an indexing variable to determine the value stored in an array of analog values
  static int final;            //Each analog value is read from a pin denoted by analogPin which should be assigned values A0 - A7 only
  static int rawVal;
 
  rawVal = analogRead(analogPin);
  final = 36 + (rawVal);

  return final;
}

int tempo1(char analogPin){
  static int raw;
  static int sec = 30000;
  static int bpm;
  static int nLength;
 
  return bpm;
}

int tempo2(char analogPin){
  static int raw;
  static int sec = 30000;
  static int bpm;
  static int nLength;



  return nLength;
}

void assignByteToPins(int pos, int a, int b, int c , int d){ // This function takes the value of an indexing variable, int pos, and assigns pis 4 - 7 the corresponding binary state
  if (pos == 0){
    digitalWrite(a, LOW);
    digitalWrite(b, LOW);
    digitalWrite(c, LOW);
    digitalWrite(d, LOW);
  }
  else if (pos == 1){
    digitalWrite(a, HIGH);
    digitalWrite(b, LOW);
    digitalWrite(c, LOW);
    digitalWrite(d, LOW);
  }
  else if (pos == 2){
    digitalWrite(a, LOW);
    digitalWrite(b, HIGH);
    digitalWrite(c, LOW);
    digitalWrite(d, LOW);
  }
  else if (pos == 3){
    digitalWrite(a, HIGH);
    digitalWrite(b, HIGH);
    digitalWrite(c, LOW);
    digitalWrite(d, LOW);
  }
  else if (pos == 4){
    digitalWrite(a, LOW);
    digitalWrite(b, LOW);
    digitalWrite(c, HIGH);
    digitalWrite(d, LOW);
  }
  else if (pos == 5){
    digitalWrite(a, HIGH);
    digitalWrite(b, LOW);
    digitalWrite(c, HIGH);
    digitalWrite(d, LOW);
  }
  else if (pos == 6){
    digitalWrite(a, LOW);
    digitalWrite(b, HIGH);
    digitalWrite(c, HIGH);
    digitalWrite(d, LOW);
  }
  else if (pos == 7){
    digitalWrite(a, HIGH);
    digitalWrite(b, HIGH);
    digitalWrite(c, HIGH);
    digitalWrite(d, LOW);
  }
  else if (pos == 8){
    digitalWrite(a, LOW);
    digitalWrite(b, LOW);
    digitalWrite(c, LOW);
    digitalWrite(d, HIGH);
  }
  else if (pos == 9){
    digitalWrite(a, HIGH);
    digitalWrite(b, LOW);
    digitalWrite(c, LOW);
    digitalWrite(d, HIGH);
  }
  else if (pos == 10){
    digitalWrite(a, LOW);
    digitalWrite(b, HIGH);
    digitalWrite(c, LOW);
    digitalWrite(d, HIGH);
  }
  else if (pos == 11){
    digitalWrite(a, HIGH);
    digitalWrite(b, HIGH);
    digitalWrite(c, LOW);
    digitalWrite(d, HIGH);
  }
  else if (pos == 12){
    digitalWrite(a, LOW);
    digitalWrite(b, LOW);
    digitalWrite(c, HIGH);
    digitalWrite(d, HIGH);
  }
  else if (pos == 13){
    digitalWrite(a, HIGH);
    digitalWrite(b, LOW);
    digitalWrite(c, HIGH);
    digitalWrite(d, HIGH);
  }
  else if (pos == 14){
    digitalWrite(a, LOW);
    digitalWrite(b, HIGH);
    digitalWrite(c, HIGH);
    digitalWrite(d, HIGH);
  }
  else if (pos == 15){
    digitalWrite(a, HIGH);
    digitalWrite(b, HIGH);
    digitalWrite(c, HIGH);
    digitalWrite(d, HIGH);
  }
}

void lcdNote(int pos, int note, int vel){
  char* intToNote[] = {"C0", "C#0", "D0", "Eb0", "E0", "F0", "F#0", "G0", "G#0", "A0", "Bb0", "B0","C1", "C#1", "D1", "Eb1", "E1", "F1", "F#1", "G1", "G#1", "A1", "Bb1", "B1",
  "C2", "C#2", "D2", "Eb2", "E2", "F2", "F#2", "G2", "G#2", "A2", "Bb2", "B2", "C3", "C#3", "D3", "Eb3", "E3", "F3", "F#3", "G3", "G#3", "A3", "Bb3", "B3",
  "C4", "C#4", "D4", "Eb4", "E4", "F4", "F#4", "G4", "G#4", "A4", "Bb4", "B4","C5", "C#5", "D5", "Eb5", "E5", "F5", "F#5", "G5", "G#5", "A5", "Bb5", "B5",
  "C6", "C#6", "D6", "Eb6", "E6", "F6", "F#6", "G6", "G#6", "A6", "Bb6", "B6", "C7", "C#7", "D7", "Eb7", "E7", "F7", "F#7", "G7", "G#7", "A7", "Bb7", "B7",
  "C8", "C#8", "D8", "Eb8", "E8", "F8", "F#8", "G8", "G#8", "A8", "Bb8", "B8","C9", "C#9", "D9", "Eb9", "E9", "F9", "F#9", "G9", "G#9", "A9", "Bb9", "B9"};
 
  lcd.clear();

  lcd.setCursor(13,0);
  lcd.print(intToNote[note]);
  lcd.setCursor(13,1);
  lcd.print(vel);
  lcd.setCursor(10, 1);
  lcd.print(pos + 1);

}

void lcdTrack(int track){
  lcd.setCursor(10,0);
  lcd.print("T");
  lcd.setCursor(11,0);
  lcd.print(track);
}

void soloMode(int channel, int pos){
    int note[16], velocity[16];
   
    assignByteToPins(pos, 14, 15, 16, 17);
   
    note[pos] = getValue16a(pos, A0);
    velocity[pos] = 99; //random(0, 2);
   
    usbMIDI.sendNoteOn(note[pos], velocity[pos], channel);
    lcdNote(pos, note[pos], velocity[pos]);
    delay(analogRead(A2));
    usbMIDI.sendNoteOff(note[pos], velocity[pos], channel);
    delay(analogRead(A2));
 
}

int allNotes[4][16];
int allVeloc[4][16];
int allLength[4][16];
int trackNum;

void seqProgram(int potChoice){
 
  if(potChoice < 100){
    trackNum = 1;
    for(int i = 0 ; i < 16 ; i++ ){
    assignByteToPins(i, 15, 14, 17, 16);
    allNotes[i][0] = getValue16a(i, A0);
    allVeloc[i][0] = 99;
    playWhileProgramming(1, i, 1);
    }
   
  }
  if(potChoice < 200){
    trackNum = 2;
    for(int i = 0 ; i < 16 ; i++ ){
    assignByteToPins(i, 15, 14, 17, 16);
    allNotes[i][1] = getValue16a(i, A0);
    allVeloc[i][1] = 99;
    playWhileProgramming(2, i, 1);
    }
  }
    if(potChoice < 300){
    trackNum = 3;
    for(int i = 0 ; i < 16 ; i++ ){
    assignByteToPins(i, 14, 15, 16, 17);
    allNotes[i][2] = getValue16a(i, A0);
    allVeloc[i][2] = 99;
    playWhileProgramming(3, i, 1);
    }
  }
    if(potChoice < 400){
    trackNum = 4;
    for(int i = 0 ; i < 16 ; i++ ){
    assignByteToPins(i, 14, 15, 16, 17);
    allNotes[i][3] = getValue16a(i, A0);
    allVeloc[i][3] = 99;
    playWhileProgramming(4, i, 1);
    }
  }
 
 
}

void playWhileProgramming(int track, int pos, int channel){
    usbMIDI.sendNoteOn(allNotes[pos][track], allVeloc[pos][track], channel);
    lcdNote(pos, allNotes[pos][track], allVeloc[pos][track]);
    lcdTrack(track);
    delay(250);
    usbMIDI.sendNoteOff(allNotes[pos][track], allVeloc[pos][track], channel);
    delay(250);
}
int count = 0;
int tcount = 0;

void programmingMode(){
 
  assignByteToPins(count, 15, 14, 17, 16);
  allNotes[tcount][count] = 36 + (analogRead(A0)/15);
  usbMIDI.sendNoteOn(allNotes[tcount][count], 99, 1);
  lcdNote(count, allNotes[tcount][count], 99);
  lcdTrack(tcount + 1);
  delay(100);
  usbMIDI.sendNoteOff(allNotes[tcount][count], 99, 1);
  delay(100);
  if(digitalRead(22) == HIGH){tcount++;}
  if(digitalRead(23) == HIGH){count++;}
  if(count == 16){count = 0;}
  if(tcount == 4){tcount = 0;}
  usbMIDI.sendNoteOff(allNotes[tcount][count], 99, 1);
 
}

void playFourTracks(int count, int vel, int channel){
  assignByteToPins(count, 14, 15, 16, 17);
  usbMIDI.sendNoteOn(allNotes[0][count], vel, channel);
  usbMIDI.sendNoteOn(allNotes[1][count], vel, channel);
  usbMIDI.sendNoteOn(allNotes[2][count], vel, channel);
  usbMIDI.sendNoteOn(allNotes[3][count], vel, channel);
  delay(analogRead(A2));
  usbMIDI.sendNoteOff(allNotes[0][count], vel, channel);
  usbMIDI.sendNoteOff(allNotes[1][count], vel, channel);
  usbMIDI.sendNoteOff(allNotes[2][count], vel, channel);
  usbMIDI.sendNoteOff(allNotes[3][count], vel, channel);
  delay(analogRead(A2));
}

//void playOneTrack(int count, int vel

void playTwoTracks(int count, int vel, int channel, int a, int b){
  assignByteToPins(count, 14, 15, 16, 17);
  usbMIDI.sendNoteOn(allNotes[a][count], vel, channel);
  usbMIDI.sendNoteOn(allNotes[b][count], vel, channel);
  delay(analogRead(A2));
  usbMIDI.sendNoteOff(allNotes[a][count], vel, channel);
  usbMIDI.sendNoteOff(allNotes[b][count], vel, channel);
  delay(analogRead(A2));
}

int wait_1;
int wait_2;
int timeStep;

void waitCalc (int bpm, int length){
  static int sec = 30000;
  int time;
 
  time = sec/bpm;
  timeStep = time/32;
 
  wait_1 = length*timeStep;
  wait_2 = time - wait_1;
 
}

int getBpmPC(){
  int bpm;
 
  return bpm;
}

 Hardware and Board Designs

The majority of hardware design was done on paper, however EAGLE Cad was used to draft and model the main schematic and printed circuit board.

 Printed Circuit Board Eagle File

Schematic Eagle file (incomplete) 


No comments:

Post a Comment