Mind the toves. They can be a bit slithy. Don't even get me started on the borogoves.

Friday, February 1, 2013

Code for Triggerhappy

Here are the sketch files, four of them: triggerhappy, Display, Encoder, and MenuSystem

triggerhappy:


//triggerhappy by Karl Gruenewald (studiokpg@gmail.com)
/*Trigger generator for modular synthesizers.
 Uses internal or external clock
 Has 6 outputs (A-F) which can each be set to one of 3 kinds of output:
 Clicktable: read through a static rhythm pattern, offset and length can be changed
 Logic: choose from 7 logic modes based on any two other outputs
 Euclidean: generate an Euclidean rhythm pattern given total length and number of groups
 Save and load settings 0-9
 (Can play, but can't store tempos slower than 59 BPM)
 Swing, +/- 99%

 IMPORTANT: When putting this software on a new Arduino, you need to comment out the loadSettings()
 call in setup(). Otherwise, you will have garbage settings and things may not work. Save the default
 settings to slot 0, then you can re-enable the loadSettings() line.

 */

#include <SoftwareSerial.h>
#include <EEPROM.h>

//Using arrays as "clicktables" to store which clicks get a trigger (1=trigger, 0=no trigger)
//Put what ever you want in here!
// 1  2  3  4| 5  6  7  8| 9  10 11 12|13 14 15 16

boolean clickTables[16][16] = {
  {
    1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0      }
  ,//1
  {
    1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0      }
  ,//2
  {
    1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0      }
  ,//3
  {
    1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1      }
  ,//4
  {
    1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0      }
  ,//5
  {
    1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0      }
  ,//6
  {
    1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1      }
  ,//7
  {
    1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0      }
  ,//8
  {
    1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1      }
  ,//9
  {
    1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1      }
  ,//10
  {
    1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0      }
  ,//11
  {
    1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0      }
  ,//12
  {
    1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1      }
  ,//13
  {
    1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0      }
  ,//14
  {
    1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0      }
  ,//15
  {
    1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0      } //16
};

byte outParam[6][10]; //10 parameters for each of the 6 outputs:
/*
 0: Mode (0, clicktable, 1, logic or 2, Euclidean)
 1: Offset (128 = no offset)
 2: 0, Trigger or 1, Gate
 3: Clicktable number
 4: Length (if clicktable)
 5: Logic mode (1-7) 1= NOT; 2= AND; 3= OR; 4= NAND; 5= NOR; 6= XOR; 7= XNOR
 6: 1st Output to base logic on (Outputs must be lower number than the output doing the logic!)
 7: 2nd Output to base logic on
 8: Euclidean length (beats)
 9: Euclidean pulses (groups)
 */
byte paramA = 0; //variable for selecting output to work on

const int offsetRange = 16; //min and max offset amount
boolean logicStore[6][offsetRange]; //store logic values for delay

#define OUT_PORT PORTB //pins 8-13 as outputs
#define PORT_DIRECTION DDRB

byte nextGate = 0; //stores binary gates to write to OUT_PORT
unsigned long nextClock = 0; //when is next pulse
unsigned long nextOffClock = 0; //when to turn triggers off
const int trigLen = 20; //length of triggers
boolean noLow = false; //only turn gates off once per clock loop
byte offGate = 0; //for turning off triggers selectively

const int clockPin = A0; //input for external clock
int clockValue = 0; //variable to store value from clock input
boolean clockInternal = true; //select internal or external clock
boolean clockPrevious = false;
boolean clock = false; //it is a clock or not?
unsigned long lastClock = 0; //for external clock calculation
const int clockThreshold = 800;
boolean seqStop = false;
boolean extClock = false;

const int buttonPin = 7; //encoder button for menu system
boolean button1;
boolean b1Pushed = false;
boolean b1Previous = false;
unsigned long lastButtonTime = 0;
const int debounceTime = 20;

const int saveButtonPin = A1; //enter save/read mode
const int buttonThreshold = 400;
int saveRead = 255;
boolean saveButton = false;
int saveSlot = 0;
const int saveDebounce = 500;
unsigned long lastSave = 0;

const int syncButtonPin = A5; //synchronize/reset patterns
boolean syncButton = false;
int syncRead = 0;
const int syncDebounce = 500; //Keep sync from happening too often
unsigned long lastSync = 0;

const int syncIntExtPin = A3; //select internal/external sync
int sourceRead = 0; //temp variable for reading int/ext switch
unsigned long switchPrevious = 0;
const int switchThreshold = 400;

//Defaults
int rateMain = 250; //internal clock interval in ms
int swing = 0; //range -99 to 99 (percent)
boolean swingToggle = 0; //swing this beat or not
float swingMod = 0; //amount to add or subtract from the beat for swing
String BPM;
int clickStep[6] = {
  0,0,0,0,0,0}; //array to track which step each pattern is on
boolean outTemp[6] = {
  false, false, false, false, false, false}; //gather output values
boolean outPrev[6] = {
  false, false, false, false, false, false}; //keep track of current output status
const int BjorkLen = 32; //max length of Euclidean pattern
boolean rhythm[BjorkLen]; //array for Euclidean pattern

extern int encoderMode; //for menu system

volatile int8_t tmpdata = 0; //variable to pass encoder value

const int txPin = 5;  // serial output pin
const int rxPin = 4;  // unused, but SoftwareSerial needs it...

SoftwareSerial mySerial =  SoftwareSerial(rxPin, txPin);


void setup() {
  Serial.begin(9600);
  setupEncoder();
  setupDisplay();
  PORT_DIRECTION = B111111;
  pinMode(buttonPin, INPUT);
  digitalWrite(buttonPin, HIGH);
  initParams();// initializes outParams array
  //IMPORTANT: comment out the following line first time you run this sketch!
  loadSettings(); //loads from save location 0 on startup (will load garbage until you do a save to slot 0!)
  pinMode(clockPin, INPUT);
  pinMode(saveButtonPin, INPUT);
  digitalWrite(saveButtonPin, HIGH);
  pinMode(syncButtonPin, INPUT);
  digitalWrite(syncButtonPin, HIGH);
  pinMode(syncIntExtPin, INPUT);
  digitalWrite(syncIntExtPin, HIGH);
  lastClock = millis();
}

void loop() {
  readInputs();
  if (tmpdata) {
    doUpdates();
    tmpdata = 0;
  }
  if (clockInternal){
    if (seqStop || extClock) {
      nextClock = millis() + rateMain;
      seqStop = false;
      extClock = false;
    }
    if ((millis() >= nextClock) ) {
      clockAction();
      nextOffClock = nextClock + trigLen; //time to make triggers go low
      swingMod = ((float)rateMain * ((float)swing / 100));
      if (swingToggle) nextClock = nextClock + rateMain + swingMod;
      else nextClock = nextClock + rateMain - swingMod;
      lastClock = millis();
      if (rateMain < 30) rateMain = 30;
      if (encoderMode == 1) {
        BPM = String(15000/rateMain);
        displayLED(BPM);
      }
      swingToggle = !swingToggle;
    }
  }

  else { //external clock
    extClock = true;
    if (clock) {
      clockAction();
      nextOffClock = nextClock + trigLen; //no swing with external clock
      nextClock = nextClock + rateMain;
      if (rateMain < 30) rateMain = 30;
      if (encoderMode == 1) {
        BPM = String(15000/rateMain);
        displayLED(BPM);
      }
    }
    if (seqStop) { //this value need to keep getting updated while seq is stopped
      nextOffClock = nextClock + trigLen;
    }
  }

  if ((millis() >= nextOffClock) && (noLow == false)) gatesLow();

  if (b1Pushed) {
    doButton();
    b1Pushed = false;
  }
  if (saveButton) {
    lastSave = millis();
    encoderMode = 14;
    displayLED("FILE");
  }
  if (syncButton) {
    lastSync = millis();
    synchronize();
  }
}


void readInputs() {

  //read encoder button
  button1 = digitalRead(buttonPin);
  if (button1) {
    if((b1Previous == false) && (millis() - lastButtonTime > debounceTime)) {
      b1Pushed = true;
      lastButtonTime = millis();
    }
    else b1Pushed = false;
    b1Previous = true;
  }
  else {
    b1Pushed = false;
    b1Previous = false;
  }

  //read file button
  saveRead = analogRead(saveButtonPin);
  if (saveRead < buttonThreshold) saveButton = false;
  else saveButton = true;
  if (saveButton && ((millis() - lastSave) > saveDebounce)) saveButton = true;
  else saveButton = false;

  //read sync button
  syncRead = analogRead(syncButtonPin);
  if (syncRead < buttonThreshold) syncButton = false;
  else syncButton = true;
  if (syncButton && ((millis() - lastSync) > syncDebounce)) syncButton = true;
  else syncButton = false;

  //read int/ext switch
  sourceRead = analogRead(syncIntExtPin);
  if (sourceRead > switchThreshold) clockInternal = true;
  else if (sourceRead <= buttonThreshold) clockInternal = false;

  //read clock pin
  if (!clockInternal) {
    clockValue = analogRead(clockPin);
    if (clockValue >= clockThreshold) {
      if (!clockPrevious) {
        clock = true;
        clockPrevious = true;
      }
      else clock = false;
    }
    else {
      clock = false;
      clockPrevious = false;
      if (millis() - lastClock > 2000) seqStop = true; //stop if no clock for 2 sec.
    }
    if (clock) {
      rateMain = millis() - lastClock;
      lastClock = millis();
      seqStop = false;
    }
  }
}


void clockAction() {
  noLow = false;
  for (int i = 0; i < 6; i++) { //go through each output A-F

    if (outParam[i][0] == 0) { //output is clicktable
      int length = outParam[i][4];
      int offset = outParam[i][1] - 128;
      if (abs(offset) > length) offset = offset % length;
      int counter = clickStep[i] + offset;
      if (counter > length - 1) counter = counter - length;
      if (counter < 0) counter = counter + length;
      int thisArray = outParam[i][3];
      outTemp[i] = clickTables[thisArray][counter];
      clickStep[i]++;
      if (clickStep[i] > length - 1) clickStep[i] = 0;
    }

    else if (outParam[i][0] == 1) { //output is logic;
      //NOTE: user must make sure logic outputs come after the ouputs they refer to, or logic will be based on previous beat
      byte baseA = outParam[i][6];//which output is base
      byte baseB = outParam[i][7];
      baseA = outTemp[baseA];//change base to value of click at that output
      baseB = outTemp[baseB];

      for (int j = offsetRange; j > 0; j--) { //Slide all the values down for delay table
        logicStore[i][j] = logicStore[i][j-1];
      }

      switch (outParam[i][5]) { //logic calculations

      case 0: //NOT
        logicStore[i][0] = !baseA;
        break;

      case 1: //AND
        logicStore[i][0] = baseA && baseB;
        break;

      case 2: //OR
        logicStore[i][0] = baseA || baseB;
        break;

      case 3: //NAND
        logicStore[i][0] = !(baseA && baseB);
        break;

      case 4: //NOR
        logicStore[i][0] = !(baseA || baseB);
        break;

      case 5: //XOR
        logicStore[i][0] = baseA ^ baseB;
        break;

      case 6: //XNOR
        logicStore[i][0] = !(baseA ^ baseB);
        break;
      }
      int offset = outParam[i][1] - 128;
      if (offset < 1) offset = 1;
      if (offset > offsetRange) offset = offsetRange;
      outTemp[i] = logicStore[i][offset - 1];
    }

    else {  //output is Euclidean
    // based on http://kreese.net/blog/2010/03/27/generating-musical-rhythms/
      int steps = outParam[i][8]; //beats
      int pulses = outParam[i][9]; //groups
      int pauses = steps - pulses;
      boolean switcher = false;
      if (pulses > pauses) {
        switcher = true;
        pauses ^= pulses;
        pulses ^= pauses;
        pauses ^= pulses;
      }
      int perPulse = floor(pauses / pulses);
      int remainder = pauses % pulses;
      int noSkip;
      if (remainder == 0) noSkip = 0;
      else noSkip = floor(pulses / remainder);
      int skipXTime;
      if (noSkip == 0) skipXTime = 0;
      else skipXTime = floor((pulses - remainder)/noSkip);

      int counter = 0;
      int skipper = 0;
      int pos = 0;
      for (int h = 1; h <= steps; h++) {
        if (counter == 0) {
          rhythm[pos] = !switcher;
          pos ++;
          counter = perPulse;

          if (remainder > 0 && skipper == 0) {
            counter++;
            remainder--;
            if (skipXTime > 0) skipper = noSkip;
            else skipper = 0;
            skipXTime--;
          }
          else {
            skipper --;
          }
        }
        else {
          rhythm[pos] = switcher;
          pos++;
          counter--;
        }
      }
         
      int c = clickStep[i] + outParam[i][1] - 128;
      c = abs(c % steps);
      outTemp[i] = rhythm[c];
      clickStep[i]++;
      if (clickStep[i] >= steps) clickStep[i] = 0;
    }
  }



  for (int k = 5; k > -1; k--) { //doing the output
    if (outParam[k][2]) {
      if (outTemp[k]) {
        outTemp[k] = !outPrev[k];
      }
      else outTemp[k] = outPrev[k];
    }
    nextGate = nextGate << 1;
    nextGate = nextGate | outTemp[k];
  }

  OUT_PORT = nextGate; //write gate outputs
  nextGate = 0;
  for (int k = 0; k < 6; k++) {
    outPrev[k] = outTemp[k];
  }
}

void gatesLow() {
  for (int k = 5; k > -1; k--) {
    offGate = offGate << 1;
    if (outParam[k][2] && outPrev[k]) {
      offGate = offGate | 1;
    }
  }
  OUT_PORT = offGate;
  offGate = 0;
  noLow = true;
}

void synchronize() { //resets all counters
  displayLED("SYNC");
  for (int i = 0; i < 6; i++) {
    clickStep[i] = 0;
  }
  swingToggle = 0;
  doUpdates();
}

void initParams() { //sets some default values for outParam
  for (int i = 0; i < 6; i++){
    for (int j = 0; j < 10; j++){
      outParam[i][j] = 0;
    }
  }
  for (int k = 0; k < 6; k++){
    outParam[k][1] = 128; //go back and set offsets to null value
  }
}

/*
 0: Mode (0, clicktable, 1, logic or 2, Euclidean)
 1: Offset (128 = no offset)
 2: 0, Trigger or 1, Gate
 3: Clicktable number
 4: Length (if clicktable)
 5: Logic mode (1-7) 1= NOT; 2= AND; 3= OR; 4= NAND; 5= NOR; 6= XOR; 7= XNOR
 6: 1st Output to base logic on
 7: 2nd Output to base logic on
 8: Euclidean length (beats)
 9: Euclidean pulses (groups)
 */

void saveSettings() { //will overwrite settings in EEPROM without warning!
  int slotByte = (saveSlot * 62); //62 paramaters to save, slotByte is first address per set
  int slotCounter = 0;
  for (int i = 0; i < 6; i++){
    for (int j = 0; j < 10; j++){
      EEPROM.write(slotByte + slotCounter, outParam[i][j]);
      slotCounter ++;
    }
  }
  EEPROM.write(slotByte + slotCounter, swing);
  slotCounter ++;
  int tempRate = (rateMain > 255) ? 255 : rateMain; //can't store more than 1 byte per EEPROM slot
  EEPROM.write(slotByte + slotCounter, tempRate); //rateMain 256 ~= 59 bpm
}


void loadSettings() {
  int slotByte = (saveSlot * 62); //62 paramaters to save, slotByte is first address per set
  int slotCounter = 0;
  for (int i = 0; i < 6; i++){
    for (int j = 0; j < 10; j++){
      outParam[i][j] = EEPROM.read(slotByte + slotCounter);
      slotCounter ++;
    }
  }
  swing = EEPROM.read(slotByte + slotCounter);
  slotCounter ++;
  rateMain = EEPROM.read(slotByte + slotCounter);
}

Display:


char charmessage[4];
int intmessage[4];

void setupDisplay()  {
  mySerial.begin(9600); // initialize communication to the display
  mySerial.write(0x7A); // command byte for brightness
  mySerial.write(0x01); // display brightness (lower = brighter)
}

void displayLED(String ledMessage)
{
  int strLength = ledMessage.length(); 
  if (strLength <= 4) {
    if (strLength < 4) mySerial.write(" ");
    if (strLength < 3) mySerial.write(" ");
    if (strLength < 2) mySerial.write(" ");
    for(int i = 0; i < strLength; i++){
      mySerial.write(ledMessage[i]);   
    }
  }
}


Encoder:

/*Rotary Encoder code from Oleg, Circuits@Home
http://www.circuitsathome.com/mcu/reading-rotary-encoder-on-arduino
plus interrupt code by "LEo" and "bikedude"
*/

#define ENC_A 2 //encoder pins
#define ENC_B 3
#define ENC_PORT PIND
int encoderCounter = 0; //variable for smoothing encoder response

void setupEncoder()
{
  /* Setup encoder pins as inputs */
  pinMode(ENC_A, INPUT);
  digitalWrite(ENC_A, HIGH);
  pinMode(ENC_B, INPUT);
  digitalWrite(ENC_B, HIGH);

  // encoder pin on interrupt 0 (pin 2)
  attachInterrupt(0, doEncoder, CHANGE);

  // encoder pin on interrupt 1 (pin 3)
  attachInterrupt(1, doEncoder, CHANGE);
}

void doEncoder()
{
  encoderCounter = encoderCounter + read_encoder();
  //make encoder less jumpy by waiting for 2 clicks in same direction!
  if (encoderCounter > 2) {
    tmpdata = 1;
    encoderCounter = 0;
  }
  if (encoderCounter < -2) {
    tmpdata = -1;
    encoderCounter = 0;
  }
}

/* returns change in encoder state (-1,0,1) */
int8_t read_encoder()
{
  static int8_t enc_states[] = {
    0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0    };
  static uint8_t old_AB = 0;
  uint8_t new_AB = ENC_PORT;

  new_AB >>= 2; //Shift the bits two positions to the right
  old_AB <<= 2;
  old_AB |= (new_AB & 0x03); //add current state
  return ( enc_states[( old_AB & 0x0f )]);
}




MenuSystem:

int mainSelector = 1; //top level
int encoderMode = 1; //menu location pointer
int mode = 0; //variable for output mode
int arrayNumber = 0; //variable for array number assignment
String tempString = ""; //variable to build display text
int arrayLength = 32; //variable to set array length
int patternOffset = 128; //variable to set pattern offset
boolean trigGate = true; //variable for trigger or gate selection
int logicMode = 0; //variable for logic mode selection
int logic1 = 0; //first logic input
int logic2 = 0; //second logic input
int EuclidBeats = 8; //Number of beats for Euclidean pattern
int EuclidGroups = 2; //Number of groups within Euclidean pattern
int saveFunction = 1; //Save or Load files

void doUpdates() //handles encoder input and display for menu system
{
  switch (encoderMode) {
 
    case 1: //Adjust Rate Main
      rateMain = rateMain - (tmpdata * rateMain/30); //make amount of change proportional to rate
      BPM = String(15000/rateMain);
      displayLED(BPM);
    break;
 
    case 2: //Output selector/Main Menu
      mainSelector = mainSelector + tmpdata;
      if (mainSelector > 8) mainSelector = 1;
      if (mainSelector < 1) mainSelector = 8;
      if (mainSelector == 1) displayLED("outA");
      else if (mainSelector == 2) displayLED("outB");
      else if (mainSelector == 3) displayLED("outC");
      else if (mainSelector == 4) displayLED("outD");
      else if (mainSelector == 5) displayLED("outE");
      else if (mainSelector == 6) displayLED("outF");
      else if (mainSelector == 7) displayLED("S3nG"); //swing ;)
      else if (mainSelector == 8) displayLED("RATE");
    break;
 
    case 3: //Select output mode
      mode = mode + tmpdata;
      if (mode > 2) mode = 0;
      if (mode < 0) mode = 2;
      if (mode == 0) displayLED("ARR ");
      else if (mode == 1) displayLED("LoG ");
      else if (mode == 2) displayLED("EuC ");
    break;
 
    case 4: //Select array number
      arrayNumber = arrayNumber + tmpdata;
      if (arrayNumber < 0) arrayNumber = 15;
      if (arrayNumber > 15) arrayNumber = 0;
      if (arrayNumber > 8) tempString = "AR";
      else tempString = "AR ";
      tempString += (arrayNumber + 1);
      displayLED(tempString);
      outParam[paramA][3] = arrayNumber;
    break;
 
    case 5: //Select array length
      arrayLength = arrayLength + tmpdata;
      if (arrayLength < 1) arrayLength = 1;
      if (arrayLength > 16) arrayLength = 16;
      if (arrayLength > 9) tempString = "AL";
      else tempString = "AL ";
      tempString += arrayLength;
      displayLED(tempString);
      outParam[paramA][4] = arrayLength;
    break;
 
    case 6: //Select pattern offset
      patternOffset = patternOffset + tmpdata;
      if (patternOffset < (128 - offsetRange)) patternOffset = (128 - offsetRange);
      if (patternOffset > (128 + offsetRange)) patternOffset = (128 + offsetRange);
      tempString = String(patternOffset - 128);
      displayLED(tempString);
      outParam[paramA][1] = patternOffset;
    break;
 
    case 7: //Select trigger or gate behavior
      trigGate = !trigGate;
      if (trigGate) displayLED("GATE");
      else displayLED("TRIG");
      outParam[paramA][2] = trigGate;
    break;
 
    case 8: //select logic mode
      logicMode = logicMode + tmpdata;
      if (logicMode < 0) logicMode = 6;
      if (logicMode > 6) logicMode = 0;
      if (logicMode == 0) displayLED("NoT ");
      else if (logicMode == 1) displayLED("AND ");
      else if (logicMode == 2) displayLED(" oR ");
      else if (logicMode == 3) displayLED("NAND");
      else if (logicMode == 4) displayLED("nor ");
      else if (logicMode == 5) displayLED("Hor ");
      else if (logicMode == 6) displayLED("Hnor");
      outParam[paramA][5] = logicMode;
    break;
 
    case 9: //select logic input 1
      logic1 = logic1 + tmpdata;
      if (logic1 < 0) logic1 = 5;
      if (logic1 > 5) logic1 = 0;
      if (logic1 == 0) displayLED("L1-A");
      else if (logic1 == 1) displayLED("L1-B");
      else if (logic1 == 2) displayLED("L1-C");
      else if (logic1 == 3) displayLED("L1-D");
      else if (logic1 == 4) displayLED("L1-E");
      else if (logic1 == 5) displayLED("L1-F");
      outParam[paramA][6] = logic1;
    break;
 
    case 10: //select logic input 2
      logic2 = logic2 + tmpdata;
      if (logic2 < 0) logic2 = 5;
      if (logic2 > 5) logic2 = 0;
      if (logic2 == 0) displayLED("L2-A");
      else if (logic2 == 1) displayLED("L2-B");
      else if (logic2 == 2) displayLED("L2-C");
      else if (logic2 == 3) displayLED("L2-D");
      else if (logic2 == 4) displayLED("L2-E");
      else if (logic2 == 5) displayLED("L2-F");
      outParam[paramA][7] = logic2;
    break;
 
    case 11: // + offset (delay)
      patternOffset = patternOffset + tmpdata;
      if (patternOffset < 128) patternOffset = 128;
      if (patternOffset > (128 + offsetRange)) patternOffset = (128 + offsetRange);
      tempString = String(patternOffset - 128) ;
      displayLED(tempString);
      outParam[paramA][1] = patternOffset;
    break;
 
    case 12: //Euclidean #beats
      EuclidBeats = EuclidBeats + tmpdata;
      if (EuclidBeats < 1) EuclidBeats = 1;
      if (EuclidBeats > 250) EuclidBeats = 250;
      if (EuclidBeats > 99) tempString = "b";
      else if (EuclidBeats > 9) tempString = "b ";
      else tempString = "b  ";
      tempString += EuclidBeats;
      displayLED(tempString);
      outParam[paramA][8] = EuclidBeats;
    break;
 
    case 13: //Euclidean #groups
      EuclidGroups = EuclidGroups + tmpdata;
      if (EuclidGroups < 1) EuclidGroups = 1;
      if (EuclidGroups > EuclidBeats) EuclidGroups = EuclidBeats;
      if (EuclidGroups > 99) tempString = "G";
      else if (EuclidGroups > 9) tempString = "G ";
      else tempString = "G  ";
      tempString += EuclidGroups;
      displayLED(tempString);
      outParam[paramA][9] = EuclidGroups;
    break;
 
    case 14: //Save & Read parameters from EEPROM
      saveFunction = saveFunction + tmpdata;
      if (saveFunction < 1) saveFunction = 3;
      if (saveFunction > 3) saveFunction = 1;
      if (saveFunction == 1) displayLED("SAUE");
      else if (saveFunction == 2) displayLED("LOAD");
      else displayLED("BACH"); //"back" ;)
    break;
 
    case 15: //Save settings
      saveSlot = saveSlot + tmpdata;
      if (saveSlot < 0) saveSlot = 0;
      if (saveSlot > 99) saveSlot = 99;
      if (saveSlot > 9) tempString = "S ";
      else tempString = "S  ";
      tempString += saveSlot;
      displayLED(tempString);
    break;
 
    case 16: //Load settings
      saveSlot = saveSlot + tmpdata;
      if (saveSlot < 0) saveSlot = 0;
      if (saveSlot > 99) saveSlot = 99;
      if (saveSlot > 9) tempString = "L ";
      else tempString = "L  ";
      tempString += saveSlot;
      displayLED(tempString);
    break;
 
    case 17: //Adjust swing
      swing = swing + tmpdata;
      if (swing < -99) swing = -99;
      if (swing > 99) swing = 99;
      if (swing < 0) tempString = "-";
      else tempString = "";
      tempString += abs(swing);
      displayLED(tempString);
    break;
  }
}

void doButton() //handles button pushes for menu system
{
  switch (encoderMode) {

  case 1: //Adjust Rate Main
      displayLED("out ");
      encoderMode = 2; //Go to Main Menu
      if (mainSelector == 1) displayLED("outA");
      else if (mainSelector == 2) displayLED("outB");
      else if (mainSelector == 3) displayLED("outC");
      else if (mainSelector == 4) displayLED("outD");
      else if (mainSelector == 5) displayLED("outE");
      else if (mainSelector == 6) displayLED("outF");
      else if (mainSelector == 7) displayLED("S3nG"); //swing ;)
      else if (mainSelector == 8) displayLED("RATE");
    break;

  case 2: //Output selector/Main Menu
      if (mainSelector < 7) {
        paramA = mainSelector - 1;
        encoderMode = 3;
     
        mode = outParam[paramA][0];
        if (mode == 0) displayLED("ARR ");
        else if (mode == 1) displayLED("LoG ");
        else if (mode == 2) displayLED("EuC ");
      }

      else if (mainSelector == 7) {
        encoderMode = 17; //Adjust swing
        if (swing < 0) tempString = "-";
        else tempString = "";
        tempString += abs(swing);
        displayLED(tempString);
      }
   
      else if (mainSelector == 8) {
        encoderMode = 1; //Go back to main rate
      }
    break;

  case 3: //Set output mode
      outParam[paramA][0] = mode;
      if (mode == 0) { //arrays
        encoderMode = 4;
        arrayNumber = outParam[paramA][3];
        if (arrayNumber > 8) tempString = "AR";
        else tempString = "AR ";
        tempString += (arrayNumber + 1);
        displayLED(tempString);
      }
      else if (mode == 1) { //logic
        encoderMode = 8;
        logicMode = outParam[paramA][5];
        if (logicMode == 0) displayLED("NoT ");
        else if (logicMode == 1) displayLED("AND ");
        else if (logicMode == 2) displayLED(" oR ");
        else if (logicMode == 3) displayLED("NAND");
        else if (logicMode == 4) displayLED("nor ");
        else if (logicMode == 5) displayLED("Hor ");
        else if (logicMode == 6) displayLED("Hnor");
      }
   
      else if (mode == 2) { //euclidean
        encoderMode = 12;
        if (EuclidBeats > 99) tempString = "b";
        else if (EuclidBeats > 9) tempString = "b ";
        else tempString = "b  ";
        tempString += EuclidBeats;
        displayLED(tempString);
      }

    break;

  case 4: //Set array number
      encoderMode = 5; //setting is done in doUpdates() to hear result right away
      arrayLength = outParam[paramA][4];
      if (arrayLength > 9) tempString = "AL";
      else tempString = "AL ";
      tempString += arrayLength;
      displayLED(tempString);
    break;

  case 5: //Set array length
      encoderMode = 6;
      patternOffset = outParam[paramA][1];
      tempString = String(patternOffset - 128);
      displayLED(tempString);
    break;

  case 6: //Set pattern offset
      encoderMode = 7;
      trigGate = outParam[paramA][2];
      if (trigGate) displayLED("GATE");
      else displayLED("TRIG");
    break;

  case 7: //Select trigger or gate
      encoderMode = 1; //go back to rate
      BPM = String(15000/rateMain);
      displayLED(BPM);
    break;

  case 8: //select logic mode
      encoderMode = 9;
      logic1 = outParam[paramA][6];
      if (logic1 == 0) displayLED("L1-A");
      else if (logic1 == 1) displayLED("L1-B");
      else if (logic1 == 2) displayLED("L1-C");
      else if (logic1 == 3) displayLED("L1-D");
      else if (logic1 == 4) displayLED("L1-E");
      else if (logic1 == 5) displayLED("L1-F");
    break;
 
  case 9: //logic input 1
      encoderMode = 10;
      logic2 = outParam[paramA][7];
      if (logic2 == 0) displayLED("L2-A");
      else if (logic2 == 1) displayLED("L2-B");
      else if (logic2 == 2) displayLED("L2-C");
      else if (logic2 == 3) displayLED("L2-D");
      else if (logic2 == 4) displayLED("L2-E");
      else if (logic2 == 5) displayLED("L2-F");
    break;
   
  case 10: //logic input 2
      encoderMode = 11;
      patternOffset = outParam[paramA][1];
      tempString = String(patternOffset - 128) ;
      displayLED(tempString);
    break;
   
  case 11: // + offset (delay)
      encoderMode = 7;
      trigGate = outParam[paramA][2];
      if (trigGate) displayLED("GATE");
      else displayLED("TRIG");
    break;
 
  case 12: //Euclidean #beats
      encoderMode = 13; //go to #groups
      EuclidGroups = outParam[paramA][9];
      if (EuclidGroups > 99) tempString = "G";
      else if (EuclidGroups > 9) tempString = "G ";
      else tempString = "G  ";
      tempString += EuclidGroups;
      displayLED(tempString);
    break;
 
  case 13: //Euclidean #groups
      encoderMode = 6; //go to adjust offset
      patternOffset = outParam[paramA][1];
      tempString = String(patternOffset - 128);
      displayLED(tempString);
    break;
 
  case 14: //Save & Read parameters from EEPROM
      if (saveFunction == 1) { //go to Save settings
        encoderMode = 15;
        if (saveSlot > 9) tempString = "S ";
        else tempString = "S  ";
        tempString += saveSlot;
        displayLED(tempString);
      }
   
      else if (saveFunction == 2) {
        encoderMode = 16; //go to Read settings
       if (saveSlot > 9) tempString = "L ";
       else tempString = "L  ";
       tempString += saveSlot;
       displayLED(tempString);
      }
   
      else encoderMode = 1; //go back to rate
      //BPM = String(15000/rateMain);
      //displayLED(BPM);
    break;

  case 15: //Save settings
      saveSettings();
      encoderMode = 1; //go back to rate
      BPM = String(15000/rateMain);
      displayLED(BPM);
    break;
 
  case 16: //Load settings
      loadSettings();
      encoderMode = 1; //go back to rate
      BPM = String(15000/rateMain);
      displayLED(BPM);
    break;
 
  case 17: //Adjust swing
      encoderMode = 1; //go back to rate
      BPM = String(15000/rateMain);
      displayLED(BPM);
    break;
 
  default:
      encoderMode = 1;
  }
}

No comments: