Padding Blowout!

Status
Not open for further replies.

Expensive Notes

Well-known member
I have a very large program which produces the following complier information:

Code:
Memory Usage on Teensy 4.1:
  FLASH: code:101396, data:373592, headers:8336   free for files:7643140
   RAM1: variables:398016, code:98248, padding:56   free for local variables:27968
   RAM2: variables:15520  free for malloc/new:508768
When I add a new function I get this information where the padding is huge all of a sudden and RAM1 is all used up:

Code:
Memory Usage on Teensy 4.1:
Multiple libraries were found for "Audio.h"  FLASH: code:101460, data:373592, headers:8272   free for files:7643140


 Used: /Applications/Teensyduino.app/Contents/Java/hardware/teensy/avr/libraries/Audio
 Not used: /Users/jmw/Documents/Electronics/Arduino/libraries/Audio_-_Adafruit_Fork
Multiple libraries were found for "MIDI.h"
 Used: /Applications/Teensyduino.app/Contents/Java/hardware/teensy/avr/libraries/MIDI
 Not used: /Users/jmw/Documents/Electronics/Arduino/libraries/MIDI_Library
Multiple libraries were found for "SD.h"
   RAM1: variables:398016, code:98312, padding:32760   free for local variables:-4800
 Used: /Applications/Teensyduino.app/Contents/Java/hardware/teensy/avr/libraries/SD
   RAM2: variables:15520  free for malloc/new:508768
Error program exceeds memory space
 Not used: /Applications/Teensyduino.app/Contents/Java/libraries/SD
exit status 255
/Applications/Teensyduino.app/Contents/Java/arduino-builder returned 255
Error compiling for board Teensy 4.1.

Here is the new Function. If I uncomment any one of the currently commented lines the padding goes from 56 to 32760 and the memory is used up

Code:
void showMIDIControllerEditExpressionPedalsScreen(){
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(2);
  display.setCursor(50, 0);
//  display.print(expIndex + 1);
//  display.setCursor(95, 1);
  display.setTextColor(SSD1306_BLACK);
//  display.fillRect(94, 0, 14, 16, SSD1306_WHITE);
//  display.print(curExpPed[expIndex].footPedal + 1);
//  display.setCursor(111, 1);
//  display.setTextColor(SSD1306_WHITE);
//  display.print(newcurExpPed.footPedal + 1);
  display.setTextSize(1);
  display.setCursor(2, 22);
  display.print("Chan");
  display.setCursor(34, 22);
  display.print("CC");
  display.setCursor(66, 22);
  display.print("Min");
  display.setCursor(98, 22);
  display.print("Max");
  display.setTextColor(SSD1306_BLACK);
  display.fillRect(0, 33, 128, 12, SSD1306_WHITE);
  display.setCursor(2, 35);
  //display.println(curExpPed[expIndex].channel);
//  display.setCursor(34, 35);
//  display.print(curExpPed[expIndex].CCType);
//  display.setCursor(66, 35);
//  display.print(curExpPed[expIndex].minValue);
//  display.setCursor(98, 35);
//  display.print(curExpPed[expIndex].maxValue);
//  display.setTextColor(SSD1306_WHITE);
//  display.setCursor(2, 52);
//  display.print(newcurExpPed.channel);
//  display.setCursor(34, 52);
//  display.print(newcurExpPed.CCType);
//  display.setCursor(66, 52);
//  display.print(newcurExpPed.minValue);
//  display.setCursor(98, 52);
//  display.print(newcurExpPed.maxValue);  
}

To me it looks like I have reached a memory limitation in some way or this is a complier problem. Not being an expert in this sort of stuff I hope someone can explain what's going on. It seems weird that the padding changes so much with the addition of a new line of code. I would expect it to gradually be used up rather than jump up so much in one go.

Here is the full code:

Code:
/* FCB 1010 Teensy 4.1 Modification  ----- TEE1010
   By John Melki-Wegner (aka Expensive Notes)
   Simple Mono Synth Version
   Three Modes:
   1. Synth - Press and release a pedal (1-8) for a note
   2. Latch Mode - Tap a Pedal to start a note - No need to hold it which is good for guitar players
   3. Arp Mode - Tap a pedal to initiate an arp - Arp pattern and length can be varied up to 8 notes
   4. Song Patterns. 100 song defined up to 32 note patterns. - Press Enter to Toggle Song and Edit modes on device - Use Pot 2 and 3 to change notes - Auto save to EEProm on leaving
   5. Drums with 11 patterns over 4 styles - Acoustic or Electronic Kit - Sub Oscillator
   Important change to reading pots. Changes can only occur after the pot is 'close' to it's previous value.
   That is you need to turn through the previous value. Allows each pot to have more than one task
   6. MIDI - Sends Clock and notes on Channel 1
   8.Song Mode with Pattern Change Code clean up and bug fixes - New Edit Mode Changer...


*/


#include <EEPROM.h>
//#include <SD.h>
//=== Audio ========================================================================= Audio


//Design Tool: https://www.pjrc.com/teensy/gui/
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>


// GUItool: begin automatically generated code
AudioPlayMemory          playMemCrash;   //xy=92,551
AudioPlayMemory          playMemRide;    //xy=94,607
AudioPlayMemory          playMemOHh;     //xy=98,506
AudioPlayMemory          playMemSnare;   //xy=99,336
AudioPlayMemory          playMemCHh;     //xy=102,454
AudioPlayMemory          playMemKick;    //xy=106,285
AudioSynthWaveform       waveformMain;      //xy=110,96
AudioSynthWaveform       waveformSub;      //xy=115,159
AudioSynthWaveform       waveformUnison;      //xy=117,205
AudioFilterLadder        ladderMain;        //xy=272,112
AudioFilterLadder        ladderUnison;        //xy=275,215
AudioEffectEnvelope      envelopeSub;      //xy=453,168
AudioEffectEnvelope      envelopeMain;      //xy=458,119
AudioMixer4              mixerDrums;         //xy=460,528
AudioEffectEnvelope      envelopeUnison;      //xy=466,220
AudioMixer4              mixerWaves;         //xy=655,176
AudioMixer4              mixerOut;         //xy=847,314
AudioOutputI2S           i2s2; //xy=1024,318
AudioConnection          patchCord1(playMemCrash, 0, mixerDrums, 2);
AudioConnection          patchCord2(playMemRide, 0, mixerDrums, 3);
AudioConnection          patchCord3(playMemOHh, 0, mixerDrums, 1);
AudioConnection          patchCord4(playMemSnare, 0, mixerOut, 2);
AudioConnection          patchCord5(playMemCHh, 0, mixerDrums, 0);
AudioConnection          patchCord6(playMemKick, 0, mixerOut, 1);
AudioConnection          patchCord7(waveformMain, 0, ladderMain, 0);
AudioConnection          patchCord8(waveformSub, envelopeSub);
AudioConnection          patchCord9(waveformUnison, 0, ladderUnison, 0);
AudioConnection          patchCord10(ladderMain, envelopeMain);
AudioConnection          patchCord11(ladderUnison, envelopeUnison);
AudioConnection          patchCord12(envelopeSub, 0, mixerWaves, 1);
AudioConnection          patchCord13(envelopeMain, 0, mixerWaves, 0);
AudioConnection          patchCord14(mixerDrums, 0, mixerOut, 3);
AudioConnection          patchCord15(envelopeUnison, 0, mixerWaves, 2);
AudioConnection          patchCord16(mixerWaves, 0, mixerOut, 0);
AudioConnection          patchCord17(mixerOut, 0, i2s2, 0);
AudioConnection          patchCord18(mixerOut, 0, i2s2, 1);
AudioControlSGTL5000     sgtl5000_1;     //xy=714,37
// GUItool: end automatically generated code


//For SD Card
const int chipSelect = BUILTIN_SDCARD;
File root = SD.open("/");


//== Drums Song Mode =========================================================================


//Sound files generated by wav2sketch program. https://www.pjrc.com/teensy/gui/?info=AudioPlayMemory
#include "AcousticKick.h"
#include "AcousticSnare.h"
#include "AcousticClosedhihat.h"
#include "AcousticOpenhihat.h"
#include "AcousticCrashcymbal.h"
#include "AcousticRidecymbal.h"


//avoid variable names starting with numbers!
#include "606Kick.h"
#include "606Snare.h"
#include "606Closedhihat.h"
#include "606Openhihat.h"
#include "606Lowtom.h"
#include "606Hightom.h"


bool drumsOn = false; //Are drums and synth playing
bool acousticDrums = true; //or electronic 606
//Using Character strings to store drum patterns so they are easy to make while coding
int barLength [5] = {24, 16, 24, 16, 16}; //for drums only
int beatType = 1; //see next line
char beatString [5] [6] = {"3/4", "4/4", "12/8", "Jazz", "Elec"};
// for 4/4 use 16 beats. See barlength above. You could probably make your own...
char kickSequence[] =      "........................";
char snareSequence[] =     "........................";
char hiHatSequence[] =     "........................";
char openHiHatSequence[] = "........................";
char crashSequence[] =     "........................";// also low tom in electronic kit
char rideSequence[] =      "........................";// also High tom in electronic kit


bool countIn = false; //standard count in for 4/4
int complexity = 4; //Drum pattern complexity. Goes from 0 to 11
int beatNum = 0;
float swing = 1.0;
bool swingLonger = false;
float swingAdjustment = 1.0;
bool canUpDateSwing = false; //Swing can be updated?
bool canUpDateDetune = false; //Detune can be updated?
bool canUpDateDrumPattern = false; // For use with Expression Pedal to change patterns
bool canUpDateKit = false;
bool canUpDateNotePosition = false; //For editting Pattern Notes
bool canUpdateNoteValue = false;


//mixer mode - to adjust volume and probability
float masterVolume = 0.4;
float volumeKnobPosition, probabilityKnobPosition, swingKnobPosition, detuneKnobPosition;//Used to retain current pot position so it can be compared to new values read from pots.
bool canUpDateBPM = true; //BPM can be updated
bool upDateVolumeProbability = false; //OK to update these
int currentSoundIndex = 0;//count through Sounds 0 = Master Volume & Probability
const char accInstrumentNames[10][6] = {"Mast", "aKick", "aSnar", "aClHH", "aOpHH", "aCrsh", "aRide", "Synt", "Uni", "Sub"}; //For display
const char elecInstrumentNames[10][6] = {"Mast", "eKick", "eSnar", "eClHH", "eOpHH", "eLTom", "eHTom", "Synt", "Uni", "Sub"};
//Volumes Match list above Kick, Snare etc...
bool canUpDateVolume[10] = {false, false, false, false, false, false, false, false, false, false}; //Should the Volume for each sound be updated.
bool canUpDateProbability[10] = {false, false, false, false, false, false, false, false, false, false}; //Should the Probability for each sound be updated
float instrumentProbability [10] = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
float instrumentVolume [10] = {1.0, 1.0, 1.0, 1.0, 0.6, 0.6, 1.0, 1.0, 1.0, 1.0};


// ==== General ===================================================================== General
//pedal modes - determine how switches and Knobs are read and what's on the screen
byte pedalMode = 12;
#define numModes  15


#define menuMode 0
#define synthMode 1
#define latchMode 2
#define arpMode 3
#define patternPlayMode 4
#define patternEditNoteMode 5
#define songPlayMode 6
#define songEditMode 7
#define songMenuMode 8
#define mixerEditSoundMode 9
#define MIDIControllerMode 10
#define MIDIControllerChoosePatch 11
#define MIDIControllerEditMode 12
#define MIDIControllerExpressionEditMode 13
#define systemMode 14


//Play Modes - determine what is playing
#define playSynth 0
#define playLatch 1
#define playArp 2
#define playPattern 3
#define playSong 4


int playMode = playSynth;


const char pedalModeNames[numModes][4] = {"Nul", "Syn", "Lat", "Arp", "P", "PE", "S", "SE", "SOS", "Mix", "MID", "M?", "MEd", "MEx", "Sys"}; // Char array number of strings then size of each string
int t1, t2; //Times to make sure the BPM delay is consistent.


// ===== Hardware =================================================================== Hardware


// ---- Analogue -------------------------------------------------------------------- Analogue
int potPin [6] = {A0, A8, A14, A15, A16, A17};   // Input pins from the 4 potentiometers and from 16 and 17 the expression pedals
int potVals [6]; //input values from pots
bool expressionPedalDrums = false;


// ---- Switches -------------------------------------------------------------------- Switches
#define numberSwitches 15


//define switch names
#define foot1 0
#define foot2 1
#define foot3 2
#define foot4 3
#define foot5 4
#define foot6 5
#define foot7 35  //should be 30 to match Video 2. I had an issue with a wire snapping off. Pin 30 was awkward to get to
#define foot8 31
#define foot9 32
#define foot10 33
#define footUp 36
#define footDown 37
#define button1 26
#define button2 27
#define button3 34
#define modeSwitch 8
#define enterSwitch 9
#define downSwitch 10
#define upSwitch 11
#define b1Switch 12
#define b2Switch 13
#define b3Switch 14


//Storage for Buttons/Foot Pedals
const int switchPins[numberSwitches] = {foot1,  foot2, foot3, foot4, foot5, foot6, foot7, foot8, foot9, foot10, footUp, footDown, button1, button2, button3}; // Note that the last five positions represent footUp, footDown and the three additional switches respectively
int previousSwitchValues [numberSwitches]; //used to avoid retriggering synth during looping - Not using bounce library due to varying length of waitTimes and due to it being a footpedal (secondary instrument)
int switchValues [numberSwitches];


//--- Oled Screen ------------------------------------------------------------------- Oled Screen
#include <Adafruit_SSD1306.h>


#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET     4 // Reset pin # 
#define SCREEN_ADDRESS 0x3C // Found using scanner
#define I2Cspeed 1000000


//Initialize OLED screen. Changed Wire to Wire2 for SCL2 and SDA2
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire2, OLED_RESET, I2Cspeed);


// === MIDI and Music ========================================================================= MIDI and Music
//Using MIDI Note numbers


#include <MIDI.h>
MIDI_CREATE_INSTANCE(HardwareSerial, Serial7, MIDI); //to send MIDI notes out
int channel = 1;
byte midi_clock = 0xf8;//sent 6 times per 8th note


//Music Stuff
byte BPM = 120;//Beats per Minute, I make the maximum 250 later
byte BPMRunningAverage [5] = {120, 120, 120, 120, 120};// To stabilize value
int BPMAverageIndex = 0; //Last BPM read into above array
int waitTime = 40; //Time between sending MIDI clock bytes
int spareTime = 40; //Time left after processes are done that needs to be padded/wasted while waiting for next beat/note


//Synth Notes
int rootNote = 36, lowestNote = 24, highestNote = 80;
//Map 128 MIDI notes to actual frequencies for A = 440Hz
const float noteFreqs[128] = {8.176, 8.662, 9.177, 9.723, 10.301, 10.913, 11.562, 12.25, 12.978, 13.75, 14.568, 15.434, 16.352, 17.324, 18.354, 19.445, 20.602, 21.827, 23.125, 24.5, 25.957, 27.5, 29.135, 30.868, 32.703, 34.648, 36.708, 38.891, 41.203, 43.654, 46.249, 48.999, 51.913, 55, 58.27, 61.735, 65.406, 69.296, 73.416, 77.782, 82.407, 87.307, 92.499, 97.999, 103.826, 110, 116.541, 123.471, 130.813, 138.591, 146.832, 155.563, 164.814, 174.614, 184.997, 195.998, 207.652, 220, 233.082, 246.942, 261.626, 277.183, 293.665, 311.127, 329.628, 349.228, 369.994, 391.995, 415.305, 440, 466.164, 493.883, 523.251, 554.365, 587.33, 622.254, 659.255, 698.456, 739.989, 783.991, 830.609, 880, 932.328, 987.767, 1046.502, 1108.731, 1174.659, 1244.508, 1318.51, 1396.913, 1479.978, 1567.982, 1661.219, 1760, 1864.655, 1975.533, 2093.005, 2217.461, 2349.318, 2489.016, 2637.02, 2793.826, 2959.955, 3135.963, 3322.438, 3520, 3729.31, 3951.066, 4186.009, 4434.922, 4698.636, 4978.032, 5274.041, 5587.652, 5919.911, 6271.927, 6644.875, 7040, 7458.62, 7902.133, 8372.018, 8869.844, 9397.273, 9956.063, 10548.08, 11175.3, 11839.82, 12543.85};
float detune = 1.000;
int cutOffFrequency = 2000;
bool canUpdateCutOffFrequency = false;


//Scale Intervals
const byte majorScale[15]         = {0, 2, 4, 5, 7, 9, 11, 12, 14, 16, 17, 19, 21, 23, 24}; //e.g. D, E, F#,G, A, B, C#, D.  Two Octaves for Arpegiator
const byte harmonicMinorScale[15] = {0, 2, 3, 5, 7, 8, 11, 12, 14, 15, 17, 19, 20, 23, 24}; //e.g. D, E, F, G, A, B♭, C, D.
bool playMajorScale = false;
const char keys [12][3] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"};//display current key. Display doesn't like the flat symbol 3 Chars needed for each element for null?
char displayChar [4]; //To display the current key to the screen


//Arpegiator
int arpRootNote = 36;
int arpLength = 4;
int currentArpNote = 0, previousArpNote = 0;;
int currentArpPattern = 0;
const byte numberArpPatterns = 8;
byte arpNotes [8] = {0, 2, 4, 2, 4, 6, 7, 4};
const byte arpPatterns[numberArpPatterns][8] = { {0, 2, 4, 7, 0, 2, 4, 6}, {7, 6, 4, 2, 0, 2, 4, 2}, {0, 2, 0, 4, 0, 6, 0, 7}, {0, 7, 0, 6, 0, 4, 0, 2}, {0, 2, 4, 2, 4, 0, 2, 4}, {0, 2, 4, 2, 4, 6, 7, 4}, {2, 4, 0, 2, 4, 7, 0, 4}, {2, 4, 0, 2, 4, 6, 7, 4}};


//Song Patterns
#define tieNote 100
#define restNote 110
#define endPattern 200
//char patternNotesName [5] = {"TV1"};
int patternRootNote = 36;
int currentPatternNote = 0;    //Note being played
int patternEditNote = 0;       //note being editted
bool patternEditMode = false;  //Editing?
int currentPatternBank = 0;
int currentPattern = 0;
// Pattern being played now
byte patternNotes [32] = {0, restNote, 0, 3, tieNote, tieNote, 0, restNote, 0, 7, 6, 5, 0, restNote, 0, 3, tieNote, tieNote, 0, restNote, 0, 7, 6, 5, endPattern, 0, 6, 0, 7, 0, 4, 0};
// Pattern queued to play next
byte nextPatternNotes [32] = {0, restNote, 0, 3, tieNote, tieNote, 0, restNote, 0, 7, 6, 5, 0, restNote, 0, 3, tieNote, tieNote, 0, restNote, 0, 7, 6, 5, endPattern, 0, 6, 0, 7, 0, 4, 0};
// "Note values" for song mode which are stored in EEprom
const byte patternValues [27] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, tieNote, restNote, endPattern };
// Display values for patternValues when editting a note
int patternValuesIndex = 0;
const char songKeys [27][4] = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B", "c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "b" , "_", "", "X"};


int tickCounter = 0;// To count MIDI clock ticks so Beats can be placed. 6 ticks to the 8th Note


//Song Mode - Linking Patterns ----------------------------------------------------


struct tsongPatterns {
  int bank, pattern, repeats;
};


//list of patterns in the current song
tsongPatterns currentSongPatterns[50];
tsongPatterns songKnobReadings;         //used to update song
int songPatternList[1000];              //Patterns played in a song
int songModeCurrentPattern = 0;         //index for list when playing song
int currentSongPatternsIndex = 1;       //Used for editing pattern list in song
char songName [5] = "XXXX";           //File Name for SD card and song name
bool firstSongLoaded = false;           //To make sure a song is loaded when going to Song Mode


char files[100][5];
int indexFiles = 0;


// === Menus ======================================================================= Menus


//Data for pedalMode menu
char menuModes[14][5] = {"Syn", "Lat", "Arp", "Patt", "PEd", "Song", "SEd", "Son?", "Mix", "MIDc", "MID?", "MIem", "MIex", "Sys"};
char menuModesTitle [8] = "Mode:";
int menuIndex = 0;


// === MIDI Controller ==============================================================
struct __attribute__((packed)) tMIDIMessage  {
  byte footPedal, channel, messageType, CCTypeOrNote, MIDIdata;
};


struct __attribute__((packed)) tExpressionPedals  {
  byte footPedal, channel, CCType, minValue, maxValue;
};


// I thought the problem was the struct so I tried this. Didn't help...
//byte ExpressionPedalNumber[20], ExpressionPedalChannel[20], ExpressionPedalCC[20], ExpressionPedalMin[20], ExpressionPedalMax[20];
//byte newExpressionPedalNumber, newExpressionPedalChannel, newExpressionPedalCC, newExpressionPedalMin, newExpressionPedalMax;


tMIDIMessage currentMIDIMessages [140];
tExpressionPedals curExpPed [20];
tMIDIMessage newCurrentMIDIMessages;
tExpressionPedals newcurExpPed;


int MIDImessagesIndex = 0, expIndex = 0;


int expPedal1min = 0, expPedal2min = 0, expPedal1max = 127, expPedal2max = 127;


// ==== SetUp ======================================================================= SetUp
void setup() {
  //USB Serial for debugging
  Serial.begin(9600);                     // Serial USB Monitor for testing
  MIDI.begin(MIDI_CHANNEL_OMNI);          // Launch MIDI and listen


  //--- Switch modes --------------------------------------------
  for (int i = 0; i < numberSwitches; i++) {
    pinMode(switchPins[i], INPUT_PULLUP);
  }


  //--- Screen ------------------------------------------------
  //   SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;); // Don't proceed, loop forever
  }


  //--- Audio -----------------------------------------------
  AudioMemory(10);// 10 is more than enough for the mono synth and one lot of drums
  sgtl5000_1.enable();                        //Using Line Out so don't need to set volume for this
  waveformMain.begin(WAVEFORM_SAWTOOTH);      //Choosing this waveform. There are many others.
  ladderMain.octaveControl(2.6);              //up 2.6 octaves (4850 Hz) & down 2.6 octaves (132 Hz)
  waveformSub.begin(WAVEFORM_TRIANGLE);       //Choosing this waveform for sub note
  waveformUnison.begin(WAVEFORM_SAWTOOTH);    //Choosing this waveform. There are many others.


  for (int i = 0; i < 10; i++) {
    switchValues[i] = HIGH; //indicate not previously pressed
    previousSwitchValues[i] = HIGH; //indicate not previously pressed
  }


  setinstrumentVolumes(masterVolume);
  //----- Probability seed - Generate Random Numbers
  randomSeed(analogRead(10));     //using a floating pin on back of Teensy - not connected


  preLoadPatternFromEEProm(0);    //Puts pattern in the pattern queue
  loadNextPattern();              //Moves loaded pattern to current playing pattern
  defaultSong();
  setUpSDCard();
  //testDataMIDIMEssages();
  loadMIDIControllerMessages();
}


// === Loop ========================================================================= Loop


void loop() {
  t1 = millis(); //start time for consistent delay


  display.clearDisplay();
  interfaceMode();
  playNoteAndOrBeat();
  display.display();                                    //Takes 28ms at 400kHz or 12ms at 1MHz (I2C)


  t2 = millis();                                        //To enable the calculation of how long have the previous tasks taken


  spareTime = waitTime * swingAdjustment - (t2 - t1);   //How much slack time is there
  while (t2 - t1 < waitTime * swingAdjustment) {        //While waiting check Expression pedals
    checkExpressionPedals();                            //checking inside this loop to improve the smoothness of filter sweeps
    t2 = millis();
  }


  Serial7.write(midi_clock);                            //Send MIDI clock every tick
  tickCounter++;
  //end of bar?
  if (tickCounter >= 24) {
    tickCounter = 0;
  }
}


// === Main Functions ===============================================================


//inputs, outputs and state changes
void interfaceMode() {
  //Do different tasks depending on the pedalMode
  if (isSwitchPressed(modeSwitch)) pedalMode = menuMode;
  switch (pedalMode) {
    case menuMode:
      changePedalModeUsingMenu();
      break;
    case synthMode:
      playMode = playSynth;
      changeOctave();
      changeScale();
      alterDetune();
      alterMasterVolume();
      showSynthModeScreen();
      break;
    case latchMode:
      playMode = playLatch;
      changeOctave();
      changeScale();
      alterDetune();
      alterMasterVolume();
      if (isSwitchPressed(enterSwitch)) turnNotesOff(rootNote + majorScale[0]); //mmmm needs fixing for MIDI out
      showSynthModeScreen();
      break;
    case arpMode:
      playMode = playArp;
      changeScale();
      alterBPM();
      alterDetune();
      alterMasterVolume();
      alterArpNoteIfFootPedalPressed();
      changeArpPatterns();
      showArpScreen();
      break;
    case patternPlayMode:
      if (playMode != playPattern) initialPatternModes();
      showMode();
      alterBPM();
      alterDetune();
      alterMasterVolume();
      alterSwing();
      changePatternIfPedalPressed();
      checkSwitchesForPatternMode();
      showPatternPlayModeScreen();
      break;
    case patternEditNoteMode:
      showMode();
      editNote();
      showPatternEditScreen();
      break;
    case songPlayMode:
      if (playMode != playSong) initialSongModes();
      showMode();
      alterBPM();
      alterDetune();
      alterMasterVolume();
      alterSwing();
      checkSwitchesForSongMode();
      if (!firstSongLoaded) loadSong();
      showPatternPlayModeScreen();
      break;
    case songEditMode:
      showSongModeScreen();
      showMode();
      checkSwitchesForSongEditMode();
      editSongKnobs();
      break;
    case songMenuMode:
      pedalMode = songPlayMode;
      showMode();
      loadSong();
      break;
    case mixerEditSoundMode:
      Serial.println("mixerEditSoundMode");
      checkSwitchesForMixerMode();
      alterIndividualVolume();
      alterIndividualProbability();
      alterKit();
      showMode();
      showMixerScreen();
      break;
    case MIDIControllerMode:
      //To do
      Serial.println("MIDIControllerMode");
      showMode();
      break;
    case MIDIControllerChoosePatch:
      //To do
      Serial.println("MIDIControllerChoosePatch");
      showMode();
      break;
    case MIDIControllerEditMode:
      showMode();
      showMIDIControllerEditMessageScreen();
      chooseMessageToEdit();
      choosePedalForMessage();
      alterPotentialValuesForFootMIDIEdit();
      saveChangesToFootMIDIEdit();
      break;
    case MIDIControllerExpressionEditMode:
      showMode();
      showMIDIControllerEditExpressionPedalsScreen();
//      chooseMessageToEdit();
//      choosePedalForMessage();
//      alterPotentialValuesForFootMIDIEdit();
//      saveChangesToFootMIDIEdit();
      break;
    case systemMode:
      //To do
      Serial.println("systemMode");
      showMode();
      break;
    default:
      // statements
      break;
  }
}


void playNoteAndOrBeat() {
  //Play notes independent of what is being altered
  switch (playMode) {
    case playSynth:
      playNoteIfPedalPressed();
      break;
    case playLatch:
      playLatchNoteIfPedalPressed();
      break;
    case playArp:
      playArpNote();
      break;
    case playPattern:
      adjustSwing();
      playAPattern();
      break;
    case playSong:
      adjustSwing();
      playAPattern();
      break;
    default:
      // statements
      break;
  }
}


// === MIDI Controller ================================================================


//struct tMIDIMessage {
//  byte footPedal, channel, messageType, CCTypeOrNote, MIDIdata;
//};
//
//struct tExpressionPedals {
//  byte footPedal, channel, CCType, minValue, maxValue;
//};
//
//tMIDIMessage currentMIDIMessages [140];
//tExpressionPedals curExpPed [20];
//tMIDIMessage newCurrentMIDIMessages;
//tExpressionPedals newcurExpPed;
//
//int MIDImessagesIndex = 0, expIndex = 0;
//int expPedal1min = 0, expPedal2min = 0, expPedal1max = 127, expPedal2max = 127;


/*
   MIDI.sendNoteOn(note, velocity, channel);
  MIDI.sendNoteOff(note, velocity, channel);
  MIDI.sendProgramChange(programNumber, channel);
  MIDI.sendControlChange(controlNumber, controlValue, channel);
*/




//byte ExpressionPedalNumber[20], ExpressionPedalChannel[20], ExpressionPedalCC[20], ExpressionPedalMin[20], ExpressionPedalMax[20];
//byte newExpressionPedalNumber, newExpressionPedalChannel, newExpressionPedalCC, newExpressionPedalMin, newExpressionPedalMax;




//setup some test data
void testDataMIDIMEssages() {
  for (int i = 0; i < 10; i++) {
    currentMIDIMessages[i].footPedal = 5;
    currentMIDIMessages[i].channel = 1;
    currentMIDIMessages[i].messageType = 11; //CC
    currentMIDIMessages[i].CCTypeOrNote = 11; //Volume
    currentMIDIMessages[i].MIDIdata = i * 10;
//    ExpressionPedalNumber[i] = 2;
//    ExpressionPedalChannel[i] = 1;
//    ExpressionPedalCC[i] = 11;
//    ExpressionPedalMin[i] = 0;
//    ExpressionPedalMax[i] = 127;
  }
  //  newCurrentMIDIMessages.footPedal = 5;
  //  newCurrentMIDIMessages.channel = 1;
  //  newCurrentMIDIMessages.messageType = 11; //CC
  //  newCurrentMIDIMessages.CCTypeOrNote = 11; //Volume
  //  newCurrentMIDIMessages.MIDIdata = 10;
}
void showMIDIControllerEditExpressionPedalsScreen(){
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(2);
  display.setCursor(50, 0);
//  display.print(expIndex + 1);
//  display.setCursor(95, 1);
  display.setTextColor(SSD1306_BLACK);
//  display.fillRect(94, 0, 14, 16, SSD1306_WHITE);
//  display.print(curExpPed[expIndex].footPedal + 1);
//  display.setCursor(111, 1);
//  display.setTextColor(SSD1306_WHITE);
//  display.print(newcurExpPed.footPedal + 1);
  display.setTextSize(1);
  display.setCursor(2, 22);
  display.print("Chan");
  display.setCursor(34, 22);
  display.print("CC");
  display.setCursor(66, 22);
  display.print("Min");
  display.setCursor(98, 22);
  display.print("Max");
  display.setTextColor(SSD1306_BLACK);
  display.fillRect(0, 33, 128, 12, SSD1306_WHITE);
  display.setCursor(2, 35);
  //display.println(curExpPed[expIndex].channel);
//  display.setCursor(34, 35);
//  display.print(curExpPed[expIndex].CCType);
//  display.setCursor(66, 35);
//  display.print(curExpPed[expIndex].minValue);
//  display.setCursor(98, 35);
//  display.print(curExpPed[expIndex].maxValue);
//  display.setTextColor(SSD1306_WHITE);
//  display.setCursor(2, 52);
//  display.print(newcurExpPed.channel);
//  display.setCursor(34, 52);
//  display.print(newcurExpPed.CCType);
//  display.setCursor(66, 52);
//  display.print(newcurExpPed.minValue);
//  display.setCursor(98, 52);
//  display.print(newcurExpPed.maxValue);  
}


void showMIDIControllerEditMessageScreen() {
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(2);
  display.setCursor(50, 0);
  display.print(MIDImessagesIndex + 1);
  display.setCursor(95, 1);
  display.setTextColor(SSD1306_BLACK);
  display.fillRect(94, 0, 14, 16, SSD1306_WHITE);
  display.print(currentMIDIMessages[MIDImessagesIndex].footPedal + 1);
  display.setCursor(111, 1);
  display.setTextColor(SSD1306_WHITE);
  display.print(newCurrentMIDIMessages.footPedal + 1);
  display.setTextSize(1);
  display.setCursor(2, 22);
  display.print("Chan");
  display.setCursor(34, 22);
  display.print("Type");
  display.setCursor(66, 22);
  display.print("Data");
  display.setCursor(98, 22);
  display.print("Data");
  display.setTextColor(SSD1306_BLACK);
  display.fillRect(0, 33, 128, 12, SSD1306_WHITE);
  display.setCursor(2, 35);
  display.print(currentMIDIMessages[MIDImessagesIndex].channel);
  display.setCursor(34, 35);
  switch (currentMIDIMessages[MIDImessagesIndex].messageType) {
    case 11:
      display.print("CC");
      break;
    case 12:
      display.print("PC");
      break;
    case 9:
      display.print("Note");
      break;
    default:
      // nothing
      break;
  }
  display.setCursor(66, 35);
  display.print(currentMIDIMessages[MIDImessagesIndex].CCTypeOrNote);
  display.setCursor(98, 35);
  display.print(currentMIDIMessages[MIDImessagesIndex].MIDIdata);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(2, 52);
  display.print(newCurrentMIDIMessages.channel);
  display.setCursor(34, 52);
  switch (newCurrentMIDIMessages.messageType) {
    case 11:
      display.print("CC");
      break;
    case 12:
      display.print("PC");
      break;
    case 9:
      display.print("Note");
      break;
    default:
      // nothing
      break;
  }
  display.setCursor(66, 52);
  display.print(newCurrentMIDIMessages.CCTypeOrNote);
  display.setCursor(98, 52);
  display.print(newCurrentMIDIMessages.MIDIdata);
}


void chooseMessageToEdit() {
  if (isSwitchPressed(downSwitch)) {
    MIDImessagesIndex--;
    if (MIDImessagesIndex < 0) MIDImessagesIndex = 139;
  }
  if (isSwitchPressed(upSwitch)) {
    MIDImessagesIndex++;
    if (MIDImessagesIndex > 139 - 1) MIDImessagesIndex = 0;
  }
  if (isSwitchPressed(b1Switch)) {
    MIDImessagesIndex--;
    if (MIDImessagesIndex < 0) MIDImessagesIndex = 139;
  }
  if (isSwitchPressed(b2Switch)) {
    MIDImessagesIndex++;
    if (MIDImessagesIndex > 139) MIDImessagesIndex = 0;
  }
}


void saveChangesToFootMIDIEdit() {
  if (isSwitchPressed(enterSwitch) || isSwitchPressed(b3Switch)) {
    currentMIDIMessages[MIDImessagesIndex].footPedal = newCurrentMIDIMessages.footPedal;
    currentMIDIMessages[MIDImessagesIndex].channel = newCurrentMIDIMessages.channel;
    currentMIDIMessages[MIDImessagesIndex].messageType = newCurrentMIDIMessages.messageType;
    currentMIDIMessages[MIDImessagesIndex].CCTypeOrNote = newCurrentMIDIMessages.CCTypeOrNote;
    currentMIDIMessages[MIDImessagesIndex].MIDIdata = newCurrentMIDIMessages.MIDIdata;
    //save to EEProm - MIDI Controller starts at 3400
    EEPROM.write(3400 + MIDImessagesIndex * 5, newCurrentMIDIMessages.footPedal);
    EEPROM.write(3400 + MIDImessagesIndex * 5 + 1, newCurrentMIDIMessages.channel);
    EEPROM.write(3400 + MIDImessagesIndex * 5 + 2, newCurrentMIDIMessages.messageType);
    EEPROM.write(3400 + MIDImessagesIndex * 5 + 3, newCurrentMIDIMessages.CCTypeOrNote);
    EEPROM.write(3400 + MIDImessagesIndex * 5 + 4, newCurrentMIDIMessages.MIDIdata);
    //check EEProm
    for (int i = 0; i < 140 * 5; i++) {
      Serial.print(3400 + i);
      Serial.print("\t");
      Serial.println(EEPROM.read(3400 + i));
    }
  }
}


void loadMIDIControllerMessages() {
  for (int i = 0; i < 140; i++) {
    currentMIDIMessages[i].footPedal = EEPROM.read(3400 + i * 5);
    currentMIDIMessages[i].channel = EEPROM.read(3400 + i * 5 + 1);
    currentMIDIMessages[i].messageType = EEPROM.read(3400 + i * 5 + 2);
    currentMIDIMessages[i].CCTypeOrNote = EEPROM.read(3400 + i * 5 + 3);
    currentMIDIMessages[i].MIDIdata = EEPROM.read(3400 + i * 5 + 4);
    Serial.println(3400 + i * 5);
  }
}


void choosePedalForMessage() {
  //Check the Switches that play a note or choose a pattern in song mode ----------------------
  for (int footPedal = 0; footPedal < 8; footPedal++) {
    switchValues[footPedal] = digitalRead(switchPins[footPedal]);
    //Switch pressed?
    if (switchJustPressed(footPedal)) {
      newCurrentMIDIMessages.footPedal = footPedal;
      previousSwitchValues[footPedal] = LOW;
    }
    if (switchJustReleased(footPedal)) {
      previousSwitchValues[footPedal] = HIGH;
    }
  }
}


void alterPotentialValuesForFootMIDIEdit() {
  potVals[0] = analogRead(potPin[0]);
  newCurrentMIDIMessages.channel = map(potVals[0], 0, 1023, 1, 16);
  potVals[1] = analogRead(potPin[1]);
  newCurrentMIDIMessages.messageType = map(potVals[1], 0, 1000, 0, 2);
  if (newCurrentMIDIMessages.messageType == 0) newCurrentMIDIMessages.messageType = 9;
  if (newCurrentMIDIMessages.messageType == 1) newCurrentMIDIMessages.messageType = 11;
  if (newCurrentMIDIMessages.messageType == 2) newCurrentMIDIMessages.messageType = 12;
  potVals[2] = analogRead(potPin[2]);
  newCurrentMIDIMessages.CCTypeOrNote = map(potVals[2], 0, 1023, 0, 127);
  potVals[3] = analogRead(potPin[3]);
  newCurrentMIDIMessages.MIDIdata = map(potVals[3], 0, 1023, 0, 127);
}


// === Menu ===========================================================================


void changePedalModeUsingMenu() {
  int value = checkMenu(menuModes, 14, menuModesTitle);
  if (value != -1) {
    pedalMode = value + 1;
  }
}


int checkMenu(char items[][5], int numItems, char title[8]) {
  //int menuIndex = -1;
  if (menuIndex > numItems - 1) menuIndex = 0;
  display.setTextSize(2);
  bool itemChosen = false;
  display.clearDisplay();
  if (menuIndex > 0) showMenuItem(0, items[menuIndex - 1], menuIndex - 1, false);
  else showTitle(title);
  showMenuItem(20, items[menuIndex], menuIndex, true);
  if (menuIndex < numItems - 1) showMenuItem(40, items[menuIndex + 1], menuIndex + 1, false);
  display.display();
  if (isSwitchPressed(upSwitch)) {
    menuIndex--;
    if (menuIndex < 0) menuIndex = numItems - 1;
  }
  if (isSwitchPressed(downSwitch)) {
    menuIndex++;
    if (menuIndex > numItems - 1) menuIndex = 0;
  }
  if (isSwitchPressed(b1Switch)) {
    menuIndex--;
    if (menuIndex < 0) menuIndex = numItems - 1;
  }
  if (isSwitchPressed(b2Switch)) {
    menuIndex++;
    if (menuIndex > numItems - 1) menuIndex = 0;
  }
  if (isSwitchPressed(enterSwitch)) itemChosen = true;
  if (isSwitchPressed(b3Switch)) itemChosen = true;
  for (int i = 0; i < 8; i++) {
    if (isSwitchPressed(i)) {
      itemChosen = true;
      menuIndex = i;
    }
  }
  if (itemChosen) return menuIndex;
  else return -1;
}


// === Synth and Latch Modes ===========================================================================


void changeOctave() {
  //down
  if (isSwitchPressed(downSwitch)) {
    rootNote = rootNote - 12;
    if (rootNote < lowestNote) rootNote = lowestNote;
  }
  //up
  if (isSwitchPressed(upSwitch)) {
    rootNote = rootNote + 12;
    if (rootNote > highestNote) rootNote = highestNote;
  }
}


void changeScale() {
  if (isSwitchPressed(b1Switch)) playMajorScale = !playMajorScale;
  if (isSwitchPressed(b2Switch)) {
    rootNote--;
    if (rootNote < lowestNote) rootNote = lowestNote;
  }
  if (isSwitchPressed(b3Switch)) {
    rootNote++;
    if (rootNote > highestNote) rootNote = highestNote;
  }
}


void alterDetune() {
  //Set other Knob functions to notupdateable
  for (int i = 1; i < 10; i++) {
    canUpDateVolume[i] = false;
    canUpDateProbability[i] = false;
  }
  canUpDateKit = false;
  canUpdateNoteValue = false;
  canUpDateNotePosition = false;


  //Alter detune?
  potVals[3] = analogRead(potPin[3]);
  float valueDetune = 1.0 + float((potVals[3])) / 50000;
  detuneKnobPosition = valueDetune;
  //Pickup - Only changes if value gets to current value
  if (valueDetune >= detune - 0.005 && valueDetune <= detune + 0.005) canUpDateDetune = true;
  if (canUpDateDetune) detune = valueDetune;
}


void alterMasterVolume() {
  potVals[1] = analogRead(potPin[1]);
  float valueVol = 2 * float((potVals[1] - 40)) / 1023;
  volumeKnobPosition = valueVol;
  if (valueVol < 0.0) valueVol = 0.0;
  //Pickup - Only changes if value gets to current value
  if (valueVol >= masterVolume - 0.05 && valueVol <= masterVolume + 0.05) canUpDateVolume[0] = true;
  if (canUpDateVolume[0])  masterVolume = valueVol;
  waveformMain.amplitude(masterVolume);
  waveformUnison.amplitude(masterVolume);
  waveformSub.amplitude(masterVolume);
}


void showSynthModeScreen() {
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  showMode();
  showKeyAndOctave();
  showKnobValue(40, 40, "Vol", int(masterVolume * 50), canUpDateVolume[0]);
  showKnobValue(100, 40, "Det", int(4000 * float(detune - 1.0)), canUpDateDetune);
}


void showKeyAndOctave() {
  //Key
  display.setCursor(90, 0);
  strcpy(displayChar, keys[(rootNote - lowestNote) % 12]);
  if (!playMajorScale) strcat(displayChar, "m");
  display.println(displayChar);
  //Octave
  display.setCursor(90, 20);
  display.println(rootNote);
}


// === ARP mode ===========================================================================


int calculateBPMRunningAverage(int newBPM) {
  BPMRunningAverage[BPMAverageIndex] = newBPM;
  BPMAverageIndex++;
  if (BPMAverageIndex > 4)BPMAverageIndex = 0;
  int total = BPMRunningAverage[0] + BPMRunningAverage[1] + BPMRunningAverage[2] + BPMRunningAverage[3] + BPMRunningAverage[4];
  return floor(total / 5);
}


void alterBPM() {
  //canUpDateFalse for controlKnobs
  potVals[0] = analogRead(potPin[0]);
  float valueBPM = map( potVals[0] , 0, 1023, 20, 250);   //sets maximum to 250
  int newBPM = calculateBPMRunningAverage(valueBPM);       // less flickering of BPM
  if (newBPM >= BPM - 5 && newBPM <= BPM + 5) canUpDateBPM = true;
  if (canUpDateBPM)  BPM = newBPM;
  //Serial.print("BPM: ");
  //Serial.println(BPM);
  waitTime = int(60000 / BPM / 12);                 //Time between MIDI clock out
}


void alterArpNoteIfFootPedalPressed() {
  //Check the Switches that play a note or choose a pattern in song mode ----------------------
  for (int footPedal = 0; footPedal < 8; footPedal++) {
    switchValues[footPedal] = digitalRead(switchPins[footPedal]);
    //Switch pressed?
    if (switchJustPressed(footPedal)) {
      arpRootNote = rootNote + footPedal;
      previousSwitchValues[footPedal] = LOW;
    }
    if (switchJustReleased(footPedal)) {
      previousSwitchValues[footPedal] = HIGH;
    }
  }
}


void changeArpPatterns() {
  //down
  if (isSwitchPressed(downSwitch)) {
    arpLength--;
    if (arpLength < 2) arpLength = 2;
  }
  //up
  if (isSwitchPressed(upSwitch)) {
    arpLength++;
    if (arpLength > 8) arpLength = 8;
  }
  if (isSwitchPressed(enterSwitch)) { //Change Arp Pattern
    currentArpPattern++;
    if (currentArpPattern >= numberArpPatterns) currentArpPattern = 0;
    for (int i = 0; i < 8; i++) {
      arpNotes[i] = arpPatterns[currentArpPattern][i];
    }
  }
}


void showArpScreen() {
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  showMode();
  display.setCursor(0, 20);
  display.println(arpLength);
  display.setCursor(20, 20);
  display.println(currentArpPattern);
  showKeyAndOctave();
  showBPM();
  showKnobValue(40, 40, "Vol", int(masterVolume * 50), canUpDateVolume[0]);
  showKnobValue(100, 40, "Det", int(4000 * float(detune - 1.0)), canUpDateDetune);
}


// === Pattern Play ===========================================================================


void alterSwing() {
  //Set edit Knob functions to notupdateable
  for (int i = 1; i < 10; i++) {
    canUpDateVolume[i] = false;
    canUpDateProbability[i] = false;
  }
  canUpDateNotePosition = false;
  canUpdateNoteValue = false;


  //Alter Swing?
  potVals[2] = analogRead(potPin[2]);
  float valueSwing = float((potVals[2])) / 1023 + 0.5;
  //make it easier to get zero
  if (valueSwing < 1.05 && valueSwing > 0.95) valueSwing = 1.00;
  swingKnobPosition = valueSwing;
  //Pickup - Only changes if value gets to current value
  if (valueSwing >= swing - 0.05 && valueSwing <= swing + 0.05) canUpDateSwing = true;
  if (canUpDateSwing) swing = valueSwing;
}


void showPatternPlayModeScreen() {
  bool foundX = false;
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(1);
  if (countIn) {
    display.setCursor(12, 0);
    display.print("#");
  }
  display.setTextSize(2);
  display.setCursor(20, 0);
  display.print(complexity);
  if (playMode == playPattern) {
    display.setCursor(85, 0);
    display.println(currentPatternBank + 1);//so 0 isn't shown
    display.setCursor(110, 0);
    display.println(currentPattern % 5 + 1);//show pattern number that matches pedal numbers
  }
  if (playMode == playSong) {
    display.setCursor(80, 0);
    display.print(songName);
  }
  showBPM();
  showKnobValue(40, 40, "Vol", int(masterVolume * 50), canUpDateVolume[0]);
  showKnobValue(70, 40, "Swi", int(swing * 50), canUpDateSwing);
  showKnobValue(100, 40, "Det", int(4000 * float(detune - 1.0)), canUpDateDetune);
  display.setTextSize(1);
  display.setCursor(50, 5);
  display.setTextColor(SSD1306_WHITE);
  display.print(beatString[beatType]);


  int x, y;
  int xStart = 0;
  int xEnd = 32;
  int yOffset = 0;
  display.setTextSize(1);
  //playing - only show half notes at a time. Gives space for knob info
  xEnd = 16;
  if (currentPatternNote > 15) {
    xStart = 16;
    xEnd = 32;
    yOffset = 20;
  }
  for (int i = xStart; i < xEnd; i++) {
    x = i % 8;
    y = int(i / 8);
    display.setCursor(x * 14 + 3, y * 9 + 20 - yOffset);
    if (!foundX) {
      if (currentPatternNote == i) display.fillRect(x * 14 + 1, y * 9 + 18 - yOffset, 15, 10, SSD1306_WHITE);
      display.setTextColor(SSD1306_WHITE);
      if (currentPatternNote == i) display.setTextColor(SSD1306_BLACK);
      switch (patternNotes[i]) {
        case tieNote:
          display.print("_");
          break;
        case restNote:
          // statements
          break;
        case endPattern:
          display.print("X");
          foundX = true;
          break;
        default:
          if (patternNotes[i] < 27) display.print(songKeys[patternNotes[i]]);
          break;
      }
    } else {
      display.print("."); //Note positions after endPattern "X"
    }
  }
}


void initialPatternModes() {
  drumsOn = false;
  playMode = playPattern;
  expressionPedalDrums = false;
}


void changePatternIfPedalPressed() {
  //Check the Switches to choose a pattern or alter drums in Pattern Mode ----------------------
  for (int footPedal = 0; footPedal < 8; footPedal++) {
    switchValues[footPedal] = digitalRead(switchPins[footPedal]);
    //Switch pressed?
    if (switchJustPressed(footPedal)) {
      playPatternMode(footPedal);
      previousSwitchValues[footPedal] = LOW;
    }
    if (switchJustReleased(footPedal)) {
      previousSwitchValues[footPedal] = HIGH;
    }
  }
}


//Each Pattern is allocated 40 bytes - 32 for notes - others below maybe
void preLoadPatternFromEEProm(int patternNumber) {
  int BPM1 = 100, BPM2 = 0;
  int patternStart = patternNumber * 34;
  BPM1 = EEPROM.read(patternStart);         //
  BPM2 = EEPROM.read(patternStart + 1);
  BPM = BPM1 + BPM2;
  if (BPM < 20) BPM = 20;
  if (BPM > 250) BPM = 250;
  for (int i = 0; i < 32; i++) {
    nextPatternNotes[i] = EEPROM.read(patternStart + i + 2);
  }
}


//Start the next bar with the queued pattern
void  loadNextPattern() {
  for (int i = 0; i < 32; i++) {
    patternNotes[i] = nextPatternNotes[i];
  }
}


void playPatternMode(int footPedal) {
  if (footPedal < 5) {
    int lastPattern = currentPattern;
    currentPattern = currentPatternBank * 5 + footPedal;
    //easter eggs
    if (switchValues[0] == LOW && switchValues[1] == LOW && !drumsOn)  {
      someoneToldAJoke();
      currentPattern = lastPattern;
    }
    if (switchValues[3] == LOW && switchValues[4] == LOW && !drumsOn)  {
      playBigEnding();
      currentPattern = lastPattern;
    }
  }
  if (footPedal == 5) {
    Serial.println(drumsOn);
    if (!drumsOn && countIn)  doTheCountIn();
    drumsOn = !drumsOn;
    beatNum = 0;
    currentPatternNote = 0;
    loadNextPattern();
  }
  //Change Drum Pattern
  if (footPedal == 6) {
    complexity--;
    if (complexity < 0) complexity = 0;
  }
  if (footPedal == 7) {
    complexity++;
    if (complexity > 11) complexity = 11;
  }
  preLoadPatternFromEEProm(currentPattern);
}


void checkSwitchesForPatternMode() {
  //enter switch
  if (isSwitchPressed(enterSwitch)) {
    //Change use of Expression Pedals
    expressionPedalDrums = !expressionPedalDrums;
  }


  //Have up or down pedals been pushed?
  if (isSwitchPressed(upSwitch)) {
    currentPatternBank++;
    if (currentPatternBank > 19) currentPatternBank = 0;
    currentPattern = currentPatternBank * 5;
    preLoadPatternFromEEProm(currentPattern);
  }
  if (isSwitchPressed(downSwitch)) {
    currentPatternBank--;
    if (currentPatternBank < 0) currentPatternBank = 19;
    currentPattern = currentPatternBank * 5;
    preLoadPatternFromEEProm(currentPattern);
  }


  if (isSwitchPressed(b1Switch)) {
    beatType++;
    if (beatType > 4) beatType = 0;
  }
  //Toggle count in
  if (isSwitchPressed(b3Switch)) countIn = !countIn;
}


// === Pattern Edit Mode ===========================================================================


void showPatternEditScreen() {
  bool foundX = false;
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(1);
  display.setTextSize(2);
  display.setCursor(85, 0);
  display.println(currentPatternBank + 1);//so 0 isn't shown
  display.setCursor(110, 0);
  display.println(currentPattern % 5 + 1);//show pattern number that matches pedal numbers


  int x, y;
  int xStart = 0;
  int xEnd = 32;
  int yOffset = 0;
  display.setTextSize(1);
  for (int i = xStart; i < xEnd; i++) {
    x = i % 8;
    y = int(i / 8);
    display.setCursor(x * 14 + 3, y * 9 + 20 - yOffset);
    if (!foundX) {
      if (patternEditNote == i) display.fillRect(x * 14 + 1, y * 9 + 18, 15, 10, SSD1306_WHITE);
      display.setTextColor(SSD1306_WHITE);
      if (patternEditNote == i) display.setTextColor(SSD1306_BLACK);
      switch (patternNotes[i]) {
        case tieNote:
          display.print("_");
          break;
        case restNote:
          // statements
          break;
        case endPattern:
          display.print("X");
          foundX = true;
          break;
        default:
          if (patternNotes[i] < 27) display.print(songKeys[patternNotes[i]]);
          break;
      }
    } else {
      display.setTextColor(SSD1306_WHITE);
      display.print("."); //Note positions after endPattern "X"
    }
  }
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(2);
  display.setCursor(50, 0);
  if (patternValuesIndex < 27) display.print(songKeys[patternValuesIndex]);
}
void showCopyScreen(int bank, int numb) {
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.print("Copy to...");
  display.setCursor(0, 20);
  display.print("Bank");
  display.setCursor(50, 20);
  display.print(bank);
  display.setCursor(0, 40);
  display.print("Numb");
  display.setCursor(50, 40);
  display.print(numb);
}


int getNumber(int numb, int bank, int footNumber, int item) {
  bool looking = true;
  int readValue;
  while (looking) {
    readValue = map(analogRead(potPin[3]), 0, 1023, 1, numb);
    showCopyScreen(bank, footNumber);
    display.setCursor(80, 20 * item);
    display.print(readValue);
    display.display();
    if (isSwitchPressed(b3Switch)) looking = false;
  }
  return readValue;
}


void saveCopiedPattern(int patternNumber) {
  int patternStart = patternNumber * 34;
  for (int i = 0; i < 32; i++) {
    EEPROM.write(patternStart + i + 2, patternNotes[i]);
  }
}


void copyPattern() {
  int bank = getNumber(20, currentPatternBank, 2, 1);
  int footNumber = getNumber(5, bank, 2, 2);
  //save
  saveCopiedPattern((bank - 1) * 5 + footNumber - 1);
}


//As a note is changed it is saved to the song pattern and to the EEprom Storage
void saveNewNote(int patternNumber) {
  int patternStart = patternNumber * 34;
  nextPatternNotes[patternEditNote] = patternValues[patternValuesIndex];  //update note on queued pattern
  patternNotes[patternEditNote] = nextPatternNotes[patternEditNote];  //update current pattern
  EEPROM.write(patternStart + patternEditNote + 2, patternNotes[patternEditNote]);
}


void editNote() {
  //Set non edit Knob functions to notupdateable
  canUpDateSwing = false;
  canUpDateDetune = false;
  canUpDateKit = false;
  for (int i = 0; i < 10; i++) {
    canUpDateVolume[i] = false;
    canUpDateProbability[i] = false;
  }


  //b2 is for copying a pattern to another location
  if (isSwitchPressed(b2Switch)) copyPattern();


  //Which Note?
  potVals[2] = analogRead(potPin[2]);
  int newpatternEditNote = map(potVals[2], 40, 1000, 0, 31);
  if (patternEditNote == newpatternEditNote) canUpDateNotePosition = true;
  if (canUpDateNotePosition) patternEditNote = newpatternEditNote;
  if (patternEditNote > 31) patternEditNote = 31;
  if (patternEditNote < 0) patternEditNote = 0;
  //What value?
  potVals[3] = analogRead(potPin[3]);
  int newpatternValuesIndex = map(potVals[3], 40, 1000, 0, 26);
  if (newpatternValuesIndex < 0) newpatternValuesIndex = 0;
  if (newpatternValuesIndex > 26) newpatternValuesIndex = 26;
  if (patternValuesIndex == newpatternValuesIndex) canUpdateNoteValue = true;
  if (canUpdateNoteValue) patternValuesIndex = newpatternValuesIndex;
  if (isSwitchPressed(enterSwitch))  saveNewNote(currentPattern);
}


// === Song Play ===========================================================================


void initialSongModes() {
  drumsOn = false;
  playMode = playSong;
  expressionPedalDrums = false;
}


//Start the next bar with the queued pattern
void  loadNextSongPattern() {
  if (songPatternList[songModeCurrentPattern] == -1) songModeCurrentPattern = 0;
  preLoadPatternFromEEProm(songPatternList[songModeCurrentPattern]);
  //Serial.println(songModeCurrentPattern);
  songModeCurrentPattern++;
  if (songModeCurrentPattern > 49) songModeCurrentPattern = 0;
  for (int i = 0; i < 32; i++) {
    patternNotes[i] = nextPatternNotes[i];
  }
}


void playSongPatternMode(int footPedal) {
  if (footPedal < 5) {
    //easter eggs
    if (switchValues[0] == LOW && switchValues[1] == LOW && !drumsOn)  {
      someoneToldAJoke();
    }
    if (switchValues[3] == LOW && switchValues[4] == LOW && !drumsOn)  {
      playBigEnding();
    }
  }
  if (footPedal == 5) {
    Serial.println(drumsOn);
    if (!drumsOn && countIn)  doTheCountIn();
    drumsOn = !drumsOn;
    beatNum = 0;
    currentPatternNote = 0;
    loadNextSongPattern();
  }
  //Change Drum Pattern
  if (footPedal == 6) {
    complexity--;
    if (complexity < 0) complexity = 0;
  }
  if (footPedal == 7) {
    complexity++;
    if (complexity > 11) complexity = 11;
  }
  preLoadPatternFromEEProm(currentPattern);
}


void checkSwitchesForSongMode() {
  //Check the Switches to choose a pattern or alter drums in Pattern Mode ----------------------
  for (int footPedal = 0; footPedal < 8; footPedal++) {
    switchValues[footPedal] = digitalRead(switchPins[footPedal]);
    //Switch pressed?
    if (switchJustPressed(footPedal)) {
      playSongPatternMode(footPedal);
      previousSwitchValues[footPedal] = LOW;
    }
    if (switchJustReleased(footPedal)) {
      previousSwitchValues[footPedal] = HIGH;
    }
  }
  //enter switch
  if (isSwitchPressed(enterSwitch)) {
    //Change use of Expression Pedals
    expressionPedalDrums = !expressionPedalDrums;
  }


  if (isSwitchPressed(b1Switch)) {
    beatType++;
    if (beatType > 4) beatType = 0;
  }
  //Toggle count in when in song mode but not mixer mode
  if (isSwitchPressed(b3Switch) && currentSoundIndex == 0) countIn = !countIn;
}


void upDateSongPatterns() {
  //Update list of song patterns
  int indx = 0;
  for (int i = 0; i < 50; i++) {
    for (int j = 0; j < currentSongPatterns[i].repeats; j++) {
      if (currentSongPatterns[i].bank == -1) songPatternList[indx] = -1;
      else songPatternList[indx] = (currentSongPatterns[i].bank) * 5 + currentSongPatterns[i].pattern;
      indx++;
      if (songPatternList[indx] != 0) {
      }
    }
  }
  songModeCurrentPattern = 0;
}


void getFileNamesFromSDCard(File dir, int numSpaces) {
  Serial.println("** SD Directory... **");
  indexFiles = 0;
  while (true) {
    File entry = dir.openNextFile();
    if (! entry) {
      Serial.println("** no more files **");
      break;
    }
    if (!entry.isDirectory()) {
      if (indexFiles < 100) {
        int len = strlen(entry.name());
        if (len < 5) strcpy(files[indexFiles] , entry.name());
      }
      Serial.println(entry.name());
      indexFiles++;
    }
    entry.close();
  }
}


void loadSong() {
  turnNotesOff(currentPatternNote);
  File root = SD.open("/");
  getFileNamesFromSDCard(root, 0);
  char title[8] = "Open..";


  sortFileNames(indexFiles);
  int menuOption = -1;
  while (menuOption == -1) {
    menuOption = checkMenu(files, indexFiles, title);
  }
  Serial.println("-- Load --");
  Serial.println(files[menuOption]);
  readBufferFromFile(files[menuOption]);
  strcpy(songName, files[menuOption]);
  upDateSongPatterns();
  firstSongLoaded = true;
}


// === Song Edit  ===========================================================================


void showSongModeScreen() {
  display.setTextSize(2);
  display.setCursor(0, 20);
  display.print(currentSongPatternsIndex + 1);
  display.setCursor(20, 0);
  display.setCursor(52, 0);
  display.setTextColor(SSD1306_WHITE);
  if (currentSongPatternsIndex > 0) {
    if (currentSongPatterns[currentSongPatternsIndex - 1].bank == -1) display.print("X");
    else display.print(currentSongPatterns[currentSongPatternsIndex - 1].bank + 1);
  }
  display.setCursor(80, 0);
  if (currentSongPatternsIndex > 0) display.print(currentSongPatterns[currentSongPatternsIndex - 1].pattern + 1);
  display.setCursor(100, 0);
  if (currentSongPatternsIndex > 0) display.print(currentSongPatterns[currentSongPatternsIndex - 1].repeats);
  display.setTextColor(SSD1306_BLACK);
  display.fillRect(44, 18, 100, 18, SSD1306_WHITE);
  display.setCursor(52, 20);
  if (currentSongPatterns[currentSongPatternsIndex].bank == -1) display.print("X");
  else display.print(currentSongPatterns[currentSongPatternsIndex].bank + 1);
  display.setCursor(80, 20);
  display.print(currentSongPatterns[currentSongPatternsIndex].pattern + 1);
  display.setCursor(100, 20);
  display.print(currentSongPatterns[currentSongPatternsIndex].repeats);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(52, 40);
  if (currentSongPatternsIndex < 49) {
    if (songKnobReadings.bank == -1) display.print("X");
    else display.print(songKnobReadings.bank + 1);
  }
  display.setCursor(80, 40);
  if (currentSongPatternsIndex < 49) display.print(songKnobReadings.pattern + 1);
  display.setCursor(100, 40);
  if (currentSongPatternsIndex < 49) display.print(songKnobReadings.repeats);
  display.setCursor(0, 40);
  display.print(songName);
}




void showChooseFilenameScreen(char newName[4]) {
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);
  display.print("Turn Knobs");
  display.setCursor(20, 30);
  display.print(newName);
  display.display();
}


void getTheSongName() {
  char letters[26] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
  bool looking = true;
  int readValue;
  while (looking) {
    readValue = map(analogRead(potPin[0]), 0, 1023, 0, 25);
    songName[0] = letters[readValue];
    readValue = map(analogRead(potPin[1]), 0, 1023, 0, 25);
    songName[1] = letters[readValue];
    readValue = map(analogRead(potPin[2]), 0, 1023, 0, 25);
    songName[2] = letters[readValue];
    readValue = map(analogRead(potPin[3]), 0, 1023, 0, 25);
    songName[3] = letters[readValue];
    showChooseFilenameScreen(songName);
    if (isSwitchPressed(b3Switch)) looking = false;
    Serial.println(songName);
  }
}


void editSongKnobs() {
  //adjust according to knobs
  //Set other Knob functions to notupdateable
  for (int i = 1; i < 10; i++) {
    canUpDateVolume[i] = false;
    canUpDateProbability[i] = false;
  }
  canUpDateKit = false;
  canUpdateNoteValue = false;
  canUpDateNotePosition = false;
  canUpDateDetune = false;


  for (int i = 1; i < 4; i++)  potVals[i] = analogRead(potPin[i]);
  songKnobReadings.bank = map(potVals[1], 0, 1023, -1, 19);
  songKnobReadings.pattern = map(potVals[2], 0, 1023, 0, 4);
  songKnobReadings.repeats = map(potVals[3], 0, 1023, 1, 20);
}


void checkSwitchesForSongEditMode() {
  //check button press
  if (isSwitchPressed(b2Switch)) {
    currentSongPatternsIndex++;
    if (currentSongPatternsIndex > 49) currentSongPatternsIndex = 0;
  }
  if (isSwitchPressed( b1Switch)) {
    currentSongPatternsIndex--;
    if (currentSongPatternsIndex < 0) currentSongPatternsIndex = 49;
  }
  if  (isSwitchPressed(b3Switch)) {
    currentSongPatterns[currentSongPatternsIndex].bank = songKnobReadings.bank;
    currentSongPatterns[currentSongPatternsIndex].pattern = songKnobReadings.pattern;
    currentSongPatterns[currentSongPatternsIndex].repeats = songKnobReadings.repeats;
  }


  if (isSwitchPressed(enterSwitch)) {
    getTheSongName();
    upDateSongPatterns();
    removeFile(songName);
    saveFile(songName);
    File root = SD.open("/");
    getFileNamesFromSDCard(root, 0);
  }
}


// === Mixer Mode ===========================================================================


void checkSwitchesForMixerMode() {
  //Go through each sound
  canUpDateVolume[0] = false;
  if (isSwitchPressed(b2Switch)) {
    if ( switchValues[13] == LOW) {
      currentSoundIndex++;
      if (currentSoundIndex >= 10) currentSoundIndex = 1;
    }
  }
}


void alterIndividualVolume() {
  canUpDateSwing = false;
  //calculate volume from knob
  potVals[1] = analogRead(potPin[1]);
  float valueVol =  2 * float((potVals[1] - 40)) / 1023;
  volumeKnobPosition = valueVol;
  if (valueVol < 0.0) valueVol = 0.0;
  //Pickup - Only changes if value gets to current value
  if (valueVol >= instrumentVolume[currentSoundIndex] - 0.05 && valueVol <= instrumentVolume[currentSoundIndex] + 0.05) {
    canUpDateVolume[currentSoundIndex] = true;
  }
  if (canUpDateVolume[currentSoundIndex]) instrumentVolume[currentSoundIndex] = valueVol;
}


void alterIndividualProbability() {
  canUpDateDetune = false;
  //calculate Probability from Knob
  potVals[2] = analogRead(potPin[2]);
  float valueProb =  float((potVals[2] - 40)) / 900;
  probabilityKnobPosition = valueProb;
  if (valueProb > 1.00) valueProb = 1.00;
  if (valueProb < 0.0) valueProb = 0.0;
  if (valueProb >= instrumentProbability[currentSoundIndex] - 0.05 && valueProb <= instrumentProbability[currentSoundIndex] + 0.05) canUpDateProbability[currentSoundIndex] = true;
  if (canUpDateProbability[currentSoundIndex]) instrumentProbability[currentSoundIndex] = valueProb;
}


void alterKit() {
  canUpDateDetune = false;
  bool newAcousticDrums;
  //electronic or acoustic kit
  potVals[3] = analogRead(potPin[3]);
  float valueKit = float((potVals[3] - 40)) / 1023;
  if (valueKit < 0.5) newAcousticDrums = true;
  else newAcousticDrums = false;
  if ((!newAcousticDrums && !acousticDrums) || (newAcousticDrums && acousticDrums) ) canUpDateKit = true;
  if (canUpDateKit) acousticDrums = newAcousticDrums;
}


void showMixerScreen() {
  //Just the stuff needed when in Mixer Mode.
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE); // Draw white text
  display.setCursor(0, 0);
  display.println("Mix");
  display.setCursor(55, 0);
  if (acousticDrums) display.println(accInstrumentNames[currentSoundIndex]);
  else  display.println(elecInstrumentNames[currentSoundIndex]);
  display.setCursor(0, 20);
  display.println("Vol");
  display.setCursor(0, 40);
  display.println("Prob");
  display.setCursor(70, 20);
  display.println(instrumentVolume[currentSoundIndex]);
  display.setCursor(70, 40);
  display.println(instrumentProbability[currentSoundIndex]);
  //Show Individual Volume
  display.drawLine(0, 57, int(60 * instrumentVolume[currentSoundIndex]), 57, SSD1306_WHITE);
  //Show Knob Position
  display.drawLine(0, 58, int(60 * volumeKnobPosition), 58, SSD1306_WHITE);
  //Show Probability
  display.drawLine(0, 62, int(60 * instrumentProbability[currentSoundIndex]), 62, SSD1306_WHITE);
  //Show Knob Position
  display.drawLine(0, 63, int(60 * probabilityKnobPosition), 63, SSD1306_WHITE);
  //display.display();
}


// === Play Synth or Latch Modes ===========================================================================


void playNoteIfPedalPressed() {
  //Check the Switches that play a note or choose a pattern in song mode ----------------------
  for (int footPedal = 0; footPedal < 8; footPedal++) {
    switchValues[footPedal] = digitalRead(switchPins[footPedal]);
    //Switch pressed?
    if (switchJustPressed(footPedal)) {
      turnNotesOff(footPedal);
      initiateNote(footPedal);
      previousSwitchValues[footPedal] = LOW;
    }
    if (switchJustReleased(footPedal)) {
      turnNotesOff(footPedal);
      previousSwitchValues[footPedal] = HIGH;
    }
  }
}


void playLatchNoteIfPedalPressed() {
  //Check the Switches that play a note or choose a pattern in song mode ----------------------
  for (int footPedal = 0; footPedal < 8; footPedal++) {
    switchValues[footPedal] = digitalRead(switchPins[footPedal]);
    //Switch pressed?
    if (switchJustPressed(footPedal)) {
      turnNotesOff(footPedal);
      initiateNote(footPedal);
      previousSwitchValues[footPedal] = LOW;
    }
    if (switchJustReleased(footPedal)) {
      previousSwitchValues[footPedal] = HIGH;
    }
  }
}


// === Play ARP Note ===========================================================================


//Playing a note in Arp Mode
void playArpNote() {
  if (tickCounter % 6 != 0) return;
  canUpDateSwing = false;
  AudioNoInterrupts();
  envelopeMain.noteOff();
  envelopeUnison.noteOff();
  envelopeSub.noteOff();
  //previousArpNote
  if (playMajorScale) {
    MIDI.sendNoteOff(arpRootNote + majorScale[arpNotes [previousArpNote]], 100, channel);
    waveformMain.frequency(noteFreqs[arpRootNote + majorScale[arpNotes [currentArpNote]]]);
    waveformUnison.frequency(noteFreqs[arpRootNote + majorScale[arpNotes [currentArpNote]]]*detune);
    waveformSub.frequency((noteFreqs[arpRootNote + majorScale[arpNotes [currentArpNote]]]) / 2);
    MIDI.sendNoteOn(arpRootNote + majorScale[arpNotes [currentArpNote]], 100, channel);
  }
  else {
    MIDI.sendNoteOff(arpRootNote + harmonicMinorScale[arpNotes [previousArpNote]], 100, channel);
    waveformMain.frequency(noteFreqs[arpRootNote + harmonicMinorScale[arpNotes [currentArpNote]]]);
    waveformUnison.frequency(noteFreqs[arpRootNote + harmonicMinorScale[arpNotes [currentArpNote]]]*detune);
    waveformSub.frequency((noteFreqs[arpRootNote + harmonicMinorScale[arpNotes [currentArpNote]]]) / 2);
    MIDI.sendNoteOn(arpRootNote + harmonicMinorScale[arpNotes [currentArpNote]], 100, channel);
  }
  envelopeMain.noteOn();
  envelopeUnison.noteOn();
  envelopeSub.noteOn();
  AudioInterrupts();
  previousArpNote = currentArpNote;
  currentArpNote++;
  if (currentArpNote >= arpLength) currentArpNote = 0;
}


// === Play Song/Pattern Notes =========================================================================== Play Notes


void playbeats() {
  //Play beats either Acoustic or Electronic kit with Probability.
  if (acousticDrums) {
    if (kickSequence[beatNum] == 'x' && random(100) <= 100 * instrumentProbability[1] ) playMemKick.play(Acoustickick);
    if (snareSequence[beatNum] == 'x' && random(100) <= 100 * instrumentProbability[2]) playMemSnare.play(AcousticSnare);
    if (hiHatSequence[beatNum] == 'x' && random(100) <= 100 * instrumentProbability[3]) playMemCHh.play(AcousticClosedhihat);
    if (openHiHatSequence[beatNum] == 'x' && random(100) <= 100 * instrumentProbability[4]) playMemOHh.play(AcousticOpenhihat);
    if (crashSequence[beatNum] == 'x' && random(100) <= 100 * instrumentProbability[5]) playMemCrash.play(AcousticCrashcymbal);
    if (rideSequence[beatNum] == 'x' && random(100) <= 100 * instrumentProbability[6]) playMemRide.play(Acousticridecymbal);
  } else {
    if (kickSequence[beatNum] == 'x' && random(100) <= 100 * instrumentProbability[1]) playMemKick.play(e606Kick);
    if (snareSequence[beatNum] == 'x' && random(100) <= 100 * instrumentProbability[2]) playMemSnare.play(e606Snare);
    if (hiHatSequence[beatNum] == 'x' && random(100) <= 100 * instrumentProbability[3]) playMemCHh.play(e606Closedhihat);
    if (openHiHatSequence[beatNum] == 'x' && random(100) <= 100 * instrumentProbability[4]) playMemOHh.play(e606Openhihat);
    if (crashSequence[beatNum] == 'x' && random(100) <= 100 * instrumentProbability[5]) playMemCrash.play(e606Lowtom);
    if (rideSequence[beatNum] == 'x' && random(100) <= 100 * instrumentProbability[6]) playMemRide.play(e606Hightom);
  }
  //choke Open HiHat if closed Hi Hat is played
  if (hiHatSequence[beatNum] == 'x') {
    if (playMemOHh.isPlaying() ) playMemOHh.stop();
  }
  beatNum++;
  if (beatNum >= barLength[beatType]) beatNum = 0;
}


//Playing a note in Synth or Latch Mode
void initiateNote(int i) {
  //Serial.println("Hi");
  canUpDateBPM = false;
  canUpDateSwing = false;
  AudioNoInterrupts(); //Disable the audio library update interrupt. This allows more than 1 object's settings to be changed simultaneously.
  if (playMajorScale) {
    waveformMain.frequency(noteFreqs[rootNote + majorScale[i]]);
    waveformUnison.frequency(noteFreqs[rootNote + majorScale[i]] * detune);
    waveformSub.frequency((noteFreqs[rootNote + majorScale[i]]) / 2);
    MIDI.sendNoteOn(rootNote + majorScale[i], 100, channel);
  }  else {
    waveformMain.frequency(noteFreqs[rootNote + harmonicMinorScale[i]]);
    waveformUnison.frequency(noteFreqs[rootNote + harmonicMinorScale[i]]* detune);
    waveformSub.frequency((noteFreqs[rootNote + harmonicMinorScale[i]]) / 2);
    MIDI.sendNoteOn(rootNote + harmonicMinorScale[i], 100, channel);
  }
  envelopeMain.noteOn();
  envelopeUnison.noteOn();
  envelopeSub.noteOn();
  AudioInterrupts();
}


//Playing a note in Pattern mode
void playPatternNote() {
  AudioNoInterrupts();
  if (patternNotes[currentPatternNote] != tieNote) { //if a tienote keep playing current note
    envelopeMain.noteOff();
    envelopeUnison.noteOff();
    envelopeSub.noteOff();
    MIDI.sendNoteOff(patternRootNote + patternNotes[currentPatternNote], 100, channel);
    if (patternNotes[currentPatternNote] < 100 ) {//only set frequency to proper notes
      waveformMain.frequency(noteFreqs[patternRootNote + patternNotes [currentPatternNote]]);
      waveformUnison.frequency(noteFreqs[patternRootNote + patternNotes [currentPatternNote]]*detune);
      waveformSub.frequency((noteFreqs[patternRootNote + patternNotes [currentPatternNote]]) / 2);
    }
    if (patternNotes[currentPatternNote] != restNote ) {
      if (random(100) <= 100 * instrumentProbability[7] ) {
        envelopeMain.noteOn(); //play if not a rest
        MIDI.sendNoteOn(patternRootNote + patternNotes[currentPatternNote], 100, channel);
      }
      if (random(100) <= 100 * instrumentProbability[8] ) envelopeUnison.noteOn(); //play if not a rest
      if (random(100) <= 100 * instrumentProbability[9] ) envelopeSub.noteOn();
    }
  }
  AudioInterrupts();
  currentPatternNote++;
  if (patternNotes[currentPatternNote] == endPattern || currentPatternNote > 31) {
    currentPatternNote = 0;
    if (playMode == playPattern) loadNextPattern();
    if (playMode == playSong) loadNextSongPattern();
  }
}


void playAPattern() {
  if (tickCounter % 6 == 0) {     // 6 MIDI clock ticks have passed
    if (drumsOn) {
      setDrums();                 //change change pattern midway through bar for interesting combinations
      playPatternNote();             //playing 8th Notes
      playbeats();
    } else {
      turnNotesOff(currentPatternNote);
    }
  }
}


void adjustSwing() {
  if (playMode < playPattern) {
    //no swing when not in song mode
    swingAdjustment = 1.00;
    return;
  }
  //Swing only in Song Mode
  if (tickCounter % 6 == 0) {
    swingLonger = !swingLonger;
    if (swingLonger) swingAdjustment = swing;
    else swingAdjustment = 1 / swing;
  }
}


// === Misc ===========================================================================


void setUpSDCard() {
  Serial.print("Initializing SD card...");
  if (!SD.begin(chipSelect)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
}


void defaultSong() {
  currentSongPatterns[0].bank = 0;
  currentSongPatterns[0].pattern = 0;
  currentSongPatterns[0].repeats = 1;
  currentSongPatterns[1].bank = 0;
  currentSongPatterns[1].pattern = 1;
  currentSongPatterns[1].repeats = 1;
  currentSongPatterns[2].bank = 0;
  currentSongPatterns[2].pattern = 2;
  currentSongPatterns[2].repeats = 1;
  currentSongPatterns[3].bank = -1;
  currentSongPatterns[3].pattern = 2;
  currentSongPatterns[3].repeats = 1;
}


void turnNotesOff(int note) {
  envelopeMain.noteOff();
  envelopeUnison.noteOff();
  envelopeSub.noteOff();
  if (playMajorScale) MIDI.sendNoteOff(rootNote + majorScale[note], 100, channel);
  else MIDI.sendNoteOff(rootNote + harmonicMinorScale[note], 100, channel);
}


bool switchJustPressed(int i) {
  if (previousSwitchValues[i] == HIGH && switchValues[i] == LOW) return true;           //just pressed
  return false;
}


bool switchJustReleased(int i) {
  if (previousSwitchValues[i] == LOW && switchValues[i] == HIGH) return true;           //Just Released
  return false;
}


//---- Check a switch -----------------------------------------------------
bool isSwitchPressed(int i) { //Checks to see if a switch has just been pressed.
  bool pressedIt = false;
  //check the switch ------------------------------
  switchValues[i] = digitalRead(switchPins[i]);
  //Switch pressed?
  if (switchJustPressed(i)) {
    pressedIt = true;
    previousSwitchValues[i] = LOW;
  }
  //Switch released?
  if (switchJustReleased(i)) {
    previousSwitchValues[i] = HIGH;
  }
  return pressedIt;
}


void setinstrumentVolumes(float masterVolume) {
  mixerOut.gain(0, 1.0);                            //From mixerWaves
  mixerOut.gain(1, masterVolume * instrumentVolume[1]);  //Kick
  mixerOut.gain(2, masterVolume * instrumentVolume[2]);  //Snare
  mixerOut.gain(3, 1.0);                            //From mixerDrums
  mixerDrums.gain(0, masterVolume * instrumentVolume[3]);  //Closed Hi Hat
  mixerDrums.gain(1, masterVolume * instrumentVolume[4]);  //Open Hi Hat
  mixerDrums.gain(2, masterVolume * instrumentVolume[5]);  //Crash
  mixerDrums.gain(3, masterVolume * instrumentVolume[6]);  //Ride
  mixerWaves.gain(0, masterVolume * instrumentVolume[7]);  //Main Synth
  mixerWaves.gain(2, masterVolume * instrumentVolume[8]);  //Unison
  mixerWaves.gain(1, masterVolume * instrumentVolume[9]);  //Sub
}




//need to check these more often due to waittime. Otherwise we get a more stepped change in sound when sweeping frequencies
void checkExpressionPedals() {
  //read expression pedals
  potVals[4] = analogRead(potPin[4]);
  potVals[5] = analogRead(potPin[5]);
  if (!expressionPedalDrums) {
    canUpDateDrumPattern = false;
    float res = float((potVals[4] - 40)) / 1000;//needs to be 0 to 1 in value
    ladderMain.resonance(res);
    ladderUnison.resonance(res);//canUpdateCutOffFrequency
    int newCutOffFrequency = potVals[5] * 5;
    if (newCutOffFrequency > cutOffFrequency - 200 && newCutOffFrequency < cutOffFrequency + 200) canUpdateCutOffFrequency = true;
    if (canUpdateCutOffFrequency) {
      cutOffFrequency = newCutOffFrequency;
      ladderUnison.frequency(cutOffFrequency);
      ladderMain.frequency(cutOffFrequency);
    }
  } else {
    canUpdateCutOffFrequency = false;
    int newComplexity = int(float(potVals[5]) / 1023 * 12);
    if (newComplexity > 12) newComplexity = 12;
    if (newComplexity < 0) newComplexity = 0;
    //Serial.println(newComplexity);
    if (newComplexity == complexity) canUpDateDrumPattern = true;
    if (canUpDateDrumPattern) {
      complexity = newComplexity;
    }
  }
}


//delete old file
void removeFile(char fileName[]) {
  Serial.println("");
  Serial.println("Remove File");
  if (SD.exists(fileName)) {
    Serial.print(fileName);
    Serial.println(" exists.");
  }
  else {
    Serial.print(fileName);
    Serial.println(" doesn't exist.");
  }


  Serial.print("Removing ...");
  Serial.println(fileName);
  SD.remove(fileName);


  if (SD.exists(fileName)) {
    Serial.print(fileName);
    Serial.println(" exists! Delete failed.");
  }
  else {
    Serial.print(fileName);
    Serial.println(" gone.");
  }
}


void saveFile(char fileName[]) {
  Serial.println("");
  Serial.print("Creating file...");
  Serial.println(fileName);
  File dataFile = SD.open(fileName, FILE_WRITE);
  // if the file is available, write to it:
  if (dataFile) {
    Serial.print("File Elements: ");
    Serial.println(dataFile.write(currentSongPatterns, 50));
    dataFile.close();
  } else {
    // if the file isn't open, pop up an error:
    Serial.println("error opening file to save");
  }
  dataFile.close();
  Serial.println("Data written.");
  Serial.println("");
}


void readBufferFromFile(char fileName[]) {
  // open the file.
  File dataFile = SD.open(fileName, FILE_READ);
  // if the file is available, read from it:
  if (dataFile) {


    dataFile.read(currentSongPatterns, 50);
    //dataFile.read(songPatternList, 1000);
    dataFile.close();
  }
  // if the file isn't open, show an error:
  else {
    Serial.println("error opening datalog.txt");
  }
  Serial.println("Data Read.");
}


void sortFileNames(int numNames) {
  for (int i = 0; i < numNames - 1; i++) {
    for (int j = 0; j < numNames - 1; j++) {
      if (strcmp(files[j + 1], files[j]) < 0) {
        char temp [5];
        strcpy(temp, files[j + 1]);
        strcpy(files[j + 1], files[j]);
        strcpy(files[j], temp);
      }
    }
  }
}


// === Screen =========================================================================== Screen


void showMode() {
  display.setTextSize(2);
  display.setCursor(0, 0);
  //Display mode
  display.println(pedalModeNames [pedalMode]); //What mode is current
}


void showBPM() {
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 40);
  display.println(BPM);
  display.setTextSize(1);
  display.setCursor(5, 56);
  display.print("BPM");
}


void showKnobValue(int x, int y, String kName, int kNumber, bool kUpDate) {
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(x, y);
  display.print(kNumber);
  display.setTextSize(1);
  display.setCursor(x + 2, y + 16);
  if (!kUpDate) {
    display.fillRect(x, y + 15, 20, 10, SSD1306_WHITE);
    display.setTextColor(SSD1306_BLACK);
  }
  display.print(kName);
}




// === Drums ===================================================


void doTheCountIn() {
  delay(waitTime * 48);
  playMemCHh.play(AcousticClosedhihat);
  delay(waitTime * 24);
  playMemCHh.play(AcousticClosedhihat);
  delay(waitTime * 24);
  playMemCHh.play(AcousticClosedhihat);
  delay(waitTime * 12);
  playMemCHh.play(AcousticClosedhihat);
  delay(waitTime * 12);
  playMemCHh.play(AcousticClosedhihat);
  delay(waitTime * 12);
  playMemCHh.play(AcousticClosedhihat);
  delay(waitTime * 12);
  tickCounter = 0; //Make sure count in beat is aligned with loop timer
}


//Drum Patterns
void setDrums() {
  if (beatType == 0) {
    switch (complexity) {
      // 3/4
      case 0:
        // statements
        strcpy(kickSequence,       "........................");
        strcpy(snareSequence,      "........................");
        strcpy(hiHatSequence,      "x.....x.....x.....x.....");
        strcpy(openHiHatSequence,  "........................");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "........................");


        break;
      case 1:
        // statements
        strcpy(kickSequence,       "........................");
        strcpy(snareSequence,      "........................");
        strcpy(hiHatSequence,      "x.x.x.x.x.x.x.x.x.x.x.x.");
        strcpy(openHiHatSequence,  "........................");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "x.......................");
        break;
      case 2:
        // statements
        strcpy(kickSequence,       "x...........x...........");
        strcpy(snareSequence,      "........................");
        strcpy(hiHatSequence,      "x.x.x.x.x.x.x.x.x.x.x.x.");
        strcpy(openHiHatSequence,  "........................");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "x.....................x.");
        break;
      case 3:
        // statements
        strcpy(kickSequence,       "x...........x...........");
        strcpy(snareSequence,      "..................x.....");
        strcpy(hiHatSequence,      "..x.x.x.x.x.x.x.x.x.x.x.");
        strcpy(openHiHatSequence,  "x.......................");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "........................");
        break;
      case 4:
        // statements
        strcpy(kickSequence,       "x...........x...........");
        strcpy(snareSequence,      "......x...........x.....");
        strcpy(hiHatSequence,      "..x.x.x.x.x.x.x.x.x.x.x.");
        strcpy(openHiHatSequence,  "x.......................");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "........................");
        break;
      case 5:
        // statements
        strcpy(kickSequence,       "x...........x...x.......");
        strcpy(snareSequence,      "......x...........x.....");
        strcpy(hiHatSequence,      "x.x.x.x.x.x.x.x.x.x.x.x.");
        strcpy(openHiHatSequence,  "x...........x...........");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "........................");
        break;
      case 6:
        // statements
        strcpy(kickSequence,       "x...........x...x.......");
        strcpy(snareSequence,      "......x.x.........x.....");
        strcpy(hiHatSequence,      "x.x.x.x.x.x.x.x.x.x.x.x.");
        strcpy(openHiHatSequence,  "........................");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "........................");
        break;
      case 7:
        // statements
        strcpy(kickSequence,       "x...........x...x.......");
        strcpy(snareSequence,      "......x...........x.....");
        strcpy(hiHatSequence,      "x.x.x.x.x.x.x.x.x.x.x.x.");
        strcpy(openHiHatSequence,  "........................");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "........................");
        break;
      case 8:
        // statements
        strcpy(kickSequence,       "x...........x...x.......");
        strcpy(snareSequence,      "......x...........x.....");
        strcpy(hiHatSequence,      "..x.x.x.x.x.x.x.x.x.x.x.");
        strcpy(openHiHatSequence,  "x.......................");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "........................");
        break;
      case 9:
        // statements
        strcpy(kickSequence,       "x...........x...x.......");
        strcpy(snareSequence,      "......x...........x.....");
        strcpy(hiHatSequence,      ".xx.x.x.x.x..xx.x.x.x.x.");
        strcpy(openHiHatSequence,  "x...........x...........");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "........................");
        break;
      case 10:
        // statements
        strcpy(kickSequence,       "x...........x...x.......");
        strcpy(snareSequence,      "......x...........x.....");
        strcpy(hiHatSequence,      "..x.x.x.x.x...x.x.x.x.x.");
        strcpy(openHiHatSequence,  "x...........x...........");
        strcpy(crashSequence,      "x.......................");
        strcpy(rideSequence,       "........................");
        break;
      case 11:
        // statements
        strcpy(kickSequence,       "x...........x...x.......");
        strcpy(snareSequence,      "......x...........x.....");
        strcpy(hiHatSequence,      ".xx.x..xx.x..xx.x..xx.x.");
        strcpy(openHiHatSequence,  "x.....x.....x.....x.....");
        strcpy(crashSequence,      "x...........x...........");
        strcpy(rideSequence,       "x.....x.....x.....x..x..");
        break;
      default:
        // statements
        break;
    }
  }
  // 4/4 beatType
  if (beatType == 1) {
    // 4/4 beatType Use 16
    switch (complexity) {
      case 0:
        // statements
        strcpy(kickSequence,       "........................");
        strcpy(snareSequence,      "........................");
        strcpy(hiHatSequence,      "x...x...x...x...........");
        strcpy(openHiHatSequence,  "........................");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "........................");


        break;
      case 1:
        // statements
        strcpy(kickSequence,       "x.......x...............");
        strcpy(snareSequence,      "........................");
        strcpy(hiHatSequence,      "x...x...x...x...........");
        strcpy(openHiHatSequence,  "........................");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "........................");
        break;
      case 2:
        // statements
        strcpy(kickSequence,       "x.......x...............");
        strcpy(snareSequence,      "....x.......x...........");
        strcpy(hiHatSequence,      "x...x...x...x...........");
        strcpy(openHiHatSequence,  "........................");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "........................");
        break;
      case 3:
        // statements
        strcpy(kickSequence,       "x.......x...............");
        strcpy(snareSequence,      "....x.......x...........");
        strcpy(hiHatSequence,      "x.x.x.x.x.x.x.x.........");
        strcpy(openHiHatSequence,  "........................");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "........................");
        break;
      case 4:
        // statements
        strcpy(kickSequence,       "x.......x.....x.........");
        strcpy(snareSequence,      "....x.......x...........");
        strcpy(hiHatSequence,      "x.x.x.x.x.x.x.x.........");
        strcpy(openHiHatSequence,  "........................");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "........................");
        break;
      case 5:
        // statements
        strcpy(kickSequence,       "x.......x...............");
        strcpy(snareSequence,      "....x......x..x.........");
        strcpy(hiHatSequence,      "x.x.x.x.x.x.x.x.........");
        strcpy(openHiHatSequence,  "........................");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "........................");
        break;
      case 6:
        // statements
        strcpy(kickSequence,       "x.......x...x...........");
        strcpy(snareSequence,      "....x.....x...x.........");
        strcpy(hiHatSequence,      "x.x.x.x.x.x.x.x.........");
        strcpy(openHiHatSequence,  "........................");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "........................");
        break;
      case 7:
        // statements
        strcpy(kickSequence,       "x.....x.x...............");
        strcpy(snareSequence,      "....x.......x.x.........");
        strcpy(hiHatSequence,      "x.x.x.x.x.x.x.x.........");
        strcpy(openHiHatSequence,  "........................");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "........................");
        break;
      case 8:
        // statements
        strcpy(kickSequence,       "x.....x.x...............");
        strcpy(snareSequence,      "....x.......x.x.........");
        strcpy(hiHatSequence,      "..xxx.x.x.x.x.x.........");
        strcpy(openHiHatSequence,  "x.......................");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "........................");
        break;
      case 9:
        // statements
        strcpy(kickSequence,       "x.....x.x...............");
        strcpy(snareSequence,      "....x.......x.x.........");
        strcpy(hiHatSequence,      "..xxx.x...xxx.x.........");
        strcpy(openHiHatSequence,  "x.......x...............");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "........................");
        break;
      case 10:
        // statements
        strcpy(kickSequence,       "x.....x.x...............");
        strcpy(snareSequence,      "....x.......x.x.........");
        strcpy(hiHatSequence,      ".xxxx.x..xxxx.x.........");
        strcpy(openHiHatSequence,  "x.......x...............");
        strcpy(crashSequence,      "x.......................");
        strcpy(rideSequence,       "........................");
        break;
      case 11:
        // statements
        strcpy(kickSequence,       "x.x.x.x.x.x.x.x.........");
        strcpy(snareSequence,      ".x.x.x.x.x.x.x.x........");
        strcpy(hiHatSequence,      ".xxx.xxx.xxx.xxx........");
        strcpy(openHiHatSequence,  "x...x...x...x...........");
        strcpy(crashSequence,      "x.......x...............");
        strcpy(rideSequence,       "........................");
        break;
      default:
        // statements
        break;
    }
  }
  // 12/8 beatType.
  if (beatType == 2) {
    switch (complexity) {
      case 0:
        // statements
        strcpy(kickSequence,       "........................");
        strcpy(snareSequence,      "........................");
        strcpy(hiHatSequence,      "x..x..x..x..x..x..x..x..");
        strcpy(openHiHatSequence,  "........................");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "x.......................");
        break;
      case 1:
        // statements
        strcpy(kickSequence,       "x...........x...........");
        strcpy(snareSequence,      "........................");
        strcpy(hiHatSequence,      "x..x..x..x..x..x..x..x..");
        strcpy(openHiHatSequence,  "........................");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "x.......................");
        break;
      case 2:
        // statements
        strcpy(kickSequence,       "x...........x...........");
        strcpy(snareSequence,      "........................");
        strcpy(hiHatSequence,      "xxxxxxxxxxxxxxxxxxxxxxxx");
        strcpy(openHiHatSequence,  "........................");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "........................");
        break;
      case 3:
        // statements
        strcpy(kickSequence,       "x.....x.....x.....x.....");
        strcpy(snareSequence,      ".........x...........x..");
        strcpy(hiHatSequence,      "xxxxxxxxxxxxxxxxxxxxxxxx");
        strcpy(openHiHatSequence,  "........................");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "........................");
        break;
      case 4:
        // statements
        strcpy(kickSequence,       "x.....x.....x.....x.....");
        strcpy(snareSequence,      "...x.....x.....x.....x..");
        strcpy(hiHatSequence,      ".xxxxxxxxxxxxxxxxxxxxxxx");
        strcpy(openHiHatSequence,  "x.......................");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "........................");
        break;
      case 5:
        // statements
        strcpy(kickSequence,       "x.....x.....x.....x.....");
        strcpy(snareSequence,      "...x.....x.....x.....x..");
        strcpy(hiHatSequence,      ".xxxxxxxxxxx.xxxxxxxxxxx");
        strcpy(openHiHatSequence,  "x...........x...........");
        strcpy(crashSequence,      "x.......................");
        strcpy(rideSequence,       "........................");
        break;
      case 6:
        // statements
        strcpy(kickSequence,       "x.....x.....x.....x.....");
        strcpy(snareSequence,      "...x.....x.....x.....x..");
        strcpy(hiHatSequence,      ".xxxxxxxxxxx.xxxxxxxxx.x");
        strcpy(openHiHatSequence,  "x...........x.........x.");
        strcpy(crashSequence,      "x.......................");
        strcpy(rideSequence,       "x.......................");
        break;
      case 7:
        // statements
        strcpy(kickSequence,       "x.....x.....x.....x.....");
        strcpy(snareSequence,      "...x.....x.....x.....x..");
        strcpy(hiHatSequence,      ".xxxxx.xxxxx.xxxxx.xxxxx");
        strcpy(openHiHatSequence,  "x.....x.....x.....x.....");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "x.......................");
        break;
      case 8:
        // statements
        strcpy(kickSequence,       "x.....x.....x.....x.....");
        strcpy(snareSequence,      "...x.....x.....x.....x..");
        strcpy(hiHatSequence,      ".xxxxx.xxxxx.xxxxx.xxxxx");
        strcpy(openHiHatSequence,  "x.....x.....x.....x.....");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "x...xxx.x.x.x.x.x.x.x.x.");
        break;
      case 9:
        // statements
        strcpy(kickSequence,       "x.....x.....x.....x.....");
        strcpy(snareSequence,      "...x.....x.....x.....x..");
        strcpy(hiHatSequence,      ".xxxxx.xxxxx.xxxxx.xxxxx");
        strcpy(openHiHatSequence,  "x.....x.....x.....x.....");
        strcpy(crashSequence,      "........................");
        strcpy(rideSequence,       "x.x.x.x.x.x.x.x.x.x.x.x.");
        break;
      case 10:
        // statements
        strcpy(kickSequence,       "x.....x.....x.....x.....");
        strcpy(snareSequence,      "...x.....x.....x.....x..");
        strcpy(hiHatSequence,      ".xxxxx.xxxxx.xxxxx.xxxxx");
        strcpy(openHiHatSequence,  "x.....x.....x.....x.....");
        strcpy(crashSequence,      "x.......................");
        strcpy(rideSequence,       "x.x.x.x.x.x.x.x.x.x.x.x.");
        break;
      case 11:
        // statements
        strcpy(kickSequence,       "x.....x.....x.....x.....");
        strcpy(snareSequence,      "...x.....x.....x.....x..");
        strcpy(hiHatSequence,      ".xxxxx.xxxxx.xxxxx.xxxxx");
        strcpy(openHiHatSequence,  "x.....x.....x.....x.....");
        strcpy(crashSequence,      "x...........x.........x.");
        strcpy(rideSequence,       "x.x.x.x.x.x.x.x.x.x.x.x.");
        break;
      default:
        // statements
        break;
    }
  }
  //Jazz
  if (beatType == 3) {
    switch (complexity) {
      case 0:
        // statements
        strcpy(kickSequence,       "x...............");
        strcpy(snareSequence,      "................");
        strcpy(hiHatSequence,      "....x.......x...");
        strcpy(openHiHatSequence,  "................");
        strcpy(crashSequence,      "................");
        strcpy(rideSequence,       "................");


        break;
      case 1:
        // statements
        strcpy(kickSequence,       "x.......x.......");
        strcpy(snareSequence,      "................");
        strcpy(hiHatSequence,      "....x.......x...");
        strcpy(openHiHatSequence,  "................");
        strcpy(crashSequence,      "................");
        strcpy(rideSequence,       "x.......x......x");
        break;
      case 2:
        // statements
        strcpy(kickSequence,       "x...x...x...x...");
        strcpy(snareSequence,      "................");
        strcpy(hiHatSequence,      "....x.......x...");
        strcpy(openHiHatSequence,  "................");
        strcpy(crashSequence,      "................");
        strcpy(rideSequence,       "x...x..xx......x");
        break;
      case 3:
        // statements
        strcpy(kickSequence,       "x...x...x...x...");
        strcpy(snareSequence,      "................");
        strcpy(hiHatSequence,      "....x.......x...");
        strcpy(openHiHatSequence,  "................");
        strcpy(crashSequence,      "................");
        strcpy(rideSequence,       "x...x..xx...x..x");
        break;
      case 4:
        // statements
        strcpy(kickSequence,       "x...x...x...x...");
        strcpy(snareSequence,      "............x...");
        strcpy(hiHatSequence,      "....x.......x...");
        strcpy(openHiHatSequence,  "................");
        strcpy(crashSequence,      "................");
        strcpy(rideSequence,       "x...x..xx...x..x");
        break;
      case 5:
        // statements
        strcpy(kickSequence,       "x...x...x...x...");
        strcpy(snareSequence,      "....x.......x...");
        strcpy(hiHatSequence,      "....x.......x...");
        strcpy(openHiHatSequence,  "................");
        strcpy(crashSequence,      "................");
        strcpy(rideSequence,       "x...x..xx...x..x");
        break;
      case 6:
        // statements
        strcpy(kickSequence,       "x...x...x...x...");
        strcpy(snareSequence,      "....x.......x...");
        strcpy(hiHatSequence,      "....x.......x...");
        strcpy(openHiHatSequence,  "................");
        strcpy(crashSequence,      "................");
        strcpy(rideSequence,       "x...x..xx..xx..x");
        break;
      case 7:
        // statements
        strcpy(kickSequence,       "x...x...x...x...");
        strcpy(snareSequence,      "....x.......x...");
        strcpy(hiHatSequence,      "....x.......x...");
        strcpy(openHiHatSequence,  "...x............");
        strcpy(crashSequence,      "................");
        strcpy(rideSequence,       "x...x..xx..xx..x");
        break;
      case 8:
        // statements
        strcpy(kickSequence,       "x...x...x...x...");
        strcpy(snareSequence,      "....x.......x...");
        strcpy(hiHatSequence,      "....x.......x...");
        strcpy(openHiHatSequence,  "...x.......x....");
        strcpy(crashSequence,      "................");
        strcpy(rideSequence,       "x..xx..xx..xx..x");
        break;
      case 9:
        // statements
        strcpy(kickSequence,       "x...x...x...x...");
        strcpy(snareSequence,      "....x.......x...");
        strcpy(hiHatSequence,      "....x.......x...");
        strcpy(openHiHatSequence,  "...x.......x....");
        strcpy(crashSequence,      "x...............");
        strcpy(rideSequence,       "x..xx..xx..xx..x");
        break;
      case 10:
        // statements
        strcpy(kickSequence,       "x...x...x...x...");
        strcpy(snareSequence,      "....x.......x...");
        strcpy(hiHatSequence,      "....x.......x...");
        strcpy(openHiHatSequence,  "...x.......x....");
        strcpy(crashSequence,      "x.......x.......");
        strcpy(rideSequence,       "x..xx..xx..xx..x");
        break;
      case 11:
        // statements
        strcpy(kickSequence,       "x...x...x...x...");
        strcpy(snareSequence,      "....x.......x...");
        strcpy(hiHatSequence,      "....x.......x...");
        strcpy(openHiHatSequence,  "...x.......x....");
        strcpy(crashSequence,      "x......xx.......");
        strcpy(rideSequence,       "x..xx..xx..xx..x");
        break;
      default:
        // statements
        break;
    }
  }
  //Electronic
  if (beatType == 4) {
    switch (complexity) {
      case 0:
        // Vanilla Ice - Ice Ice Baby
        strcpy(kickSequence,        "x.....x...x...x.");
        strcpy(snareSequence,       "....x.......x...");
        strcpy(hiHatSequence,       "x.x.x.x.x.x.x.x.");
        strcpy(openHiHatSequence,   "................");
        strcpy(crashSequence,       "................");
        strcpy(rideSequence,        "................");
        break;
      case 1:
        // basic
        strcpy(kickSequence,        "x.......x.......");
        strcpy(snareSequence,       "....x.......x...");
        strcpy(hiHatSequence,       "x.x.x.x.x.x.x.x.");
        strcpy(openHiHatSequence,   "................");
        strcpy(crashSequence,       "..........x...x.");
        strcpy(rideSequence,        "............x...");
        break;
      case 2:
        // basic plus
        strcpy(kickSequence,        "x.......x.......");
        strcpy(snareSequence,       "....x.......x...");
        strcpy(hiHatSequence,       ".xx...x.x.x.x.x.");
        strcpy(openHiHatSequence,   "x...x...........");
        strcpy(crashSequence,       "..........x...x.");
        strcpy(rideSequence,        "............x...");
        break;
      case 3:
        // Phil Collins - In the Air Tonight
        strcpy(kickSequence,        "..........x.....");
        strcpy(snareSequence,       "............x...");
        strcpy(hiHatSequence,       "..x...x...x...x.");
        strcpy(openHiHatSequence,   "x...........x...");
        strcpy(crashSequence,       "................");
        strcpy(rideSequence,        "..x...x.........");
        break;
      case 4:
        // Amen Break
        strcpy(kickSequence,        "x.x.......xx....");
        strcpy(snareSequence,       "....x..x.x..x...");
        strcpy(hiHatSequence,       "x.x.x.x.x.x.x.x.");
        strcpy(openHiHatSequence,   "................");
        strcpy(crashSequence,       "................");
        strcpy(rideSequence,        "................");
        break;
      case 5:
        // Genesis Mama
        strcpy(kickSequence,        "x.....x.x.....x.");
        strcpy(snareSequence,       "....x.......x...");
        strcpy(hiHatSequence,       "xxxxxxxxxxx.xxxx");
        strcpy(openHiHatSequence,   "...........x....");
        strcpy(crashSequence,       "................");
        strcpy(rideSequence,        "................");
        break;
      case 6:
        // Genesis - Man on the Corner
        strcpy(kickSequence,        "..x...x.x.x.x...");
        strcpy(snareSequence,       "................");
        strcpy(hiHatSequence,       "x.x.....x.xxx.x.");
        strcpy(openHiHatSequence,   "................");
        strcpy(crashSequence,       "x...x.....x.....");
        strcpy(rideSequence,        "..x....x..xxx.x.");
        break;
      case 7:
        // Marvin Gaye - Sexual Healing
        strcpy(kickSequence,        "x......xx.x.x..x");
        strcpy(snareSequence,       "....x.....xx....");
        strcpy(hiHatSequence,       "x.xxx.x.x.x.x.xx");
        strcpy(openHiHatSequence,   "......x.........");
        strcpy(crashSequence,       "x..x..x.........");
        strcpy(rideSequence,        "..x.............");
        break;
      case 8:
        // Falco - Jeanny
        strcpy(kickSequence,        "x......xx.......");
        strcpy(snareSequence,       "....x.......x...");
        strcpy(hiHatSequence,       "x.x.x.x.x.x.x.x.");
        strcpy(openHiHatSequence,   "................");
        strcpy(crashSequence,       ".....x..........");
        strcpy(rideSequence,        ".x.x............");
        break;
      case 9:
        // Madonna - Vogue
        strcpy(kickSequence,        "x...x...x...x...");
        strcpy(snareSequence,       "....x..x....x...");
        strcpy(hiHatSequence,       "x..xx.x.x..xx.x.");
        strcpy(openHiHatSequence,   "..x.......x.....");
        strcpy(crashSequence,       "................");
        strcpy(rideSequence,        "................");
        break;
      case 10:
        // James Brown - Funky
        strcpy(kickSequence,        "x.x...x...x..x..");
        strcpy(snareSequence,       "....x..x.x.xx..x");
        strcpy(hiHatSequence,       "xxxxxxx.xxxxx.xx");
        strcpy(openHiHatSequence,   ".......x.....x..");
        strcpy(crashSequence,       "................");
        strcpy(rideSequence,        "................");
        break;
      case 11:
        // Mantronix - Needle to the Groove
        strcpy(kickSequence,        "x..x..x.x.......");
        strcpy(snareSequence,       "....x.......x...");
        strcpy(hiHatSequence,       "x.x.x.x.x.x.x.x.");
        strcpy(openHiHatSequence,   ".x.x.x.x.x.x.x.x");
        strcpy(crashSequence,       "..xx..xx.x.x..x.");
        strcpy(rideSequence,        "..x..x..x..x..x.");
        break;
      default:
        // statements
        break;
    }
  }
}


// === Menus =================================================================== Menus


void showMenuItem(int y, char displayItem[], int index, bool selected) {
  char temp[10];
  display.setCursor(10, y);
  if (selected) {
    display.setTextColor(SSD1306_BLACK);
    display.fillRect(9, y - 1, 100, 16, SSD1306_WHITE);
  } else display.setTextColor(SSD1306_WHITE);
  if (index < 9) {
    itoa(index + 1, temp, 10);
    strcat(temp, " ");
    strcat(temp, displayItem);
    display.print(temp);
  } else display.print(displayItem);
}


void showTitle(char title[]) {
  display.setCursor(10, 0);
  display.setTextColor(SSD1306_WHITE);
  //display.fillRect(9, 0, 100, 16, SSD1306_WHITE);
  display.print(title);
}


//=== easter eggs ================================================================
void someoneToldAJoke() {
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE); // Draw white text
  display.setCursor(0, 20);
  display.println("badum tish");
  display.display();
  //Boom Tish
  playMemKick.play(Acoustickick);
  delay(100);
  playMemSnare.play(AcousticSnare);
  delay(200);
  playMemCrash.play(AcousticCrashcymbal);
  delay(3000);
}


void playBigEnding() {
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(SSD1306_WHITE); // Draw white text
  display.setCursor(0, 20);
  display.println("BIG finish");
  display.display();
  int waitTime = 1500;
  playMemKick.play(Acoustickick);
  delay(waitTime / 3);
  playMemKick.play(Acoustickick);
  delay(waitTime / 3);
  playMemSnare.play(AcousticSnare);
  delay(waitTime / 3);
  playMemKick.play(Acoustickick);
  delay(waitTime / 3);
  playMemKick.play(Acoustickick);
  delay(waitTime / 3);
  playMemSnare.play(AcousticSnare);
  delay(waitTime);


  playMemKick.play(Acoustickick);
  playMemSnare.play(AcousticSnare);
  playMemOHh.play(AcousticOpenhihat);
  delay(waitTime);


  playMemSnare.play(AcousticSnare);
  playMemOHh.play(AcousticOpenhihat);
  delay(waitTime);


  playMemKick.play(Acoustickick);
  playMemSnare.play(AcousticSnare);
  playMemCrash.play(AcousticCrashcymbal);
  delay(waitTime * 2);
}
 
Details here : pjrc.com/store/teensy41.html#memory

At least in some fashion ... the map and memory info and these ...
...
FASTRUN - Functions defined with "FASTRUN" are allocated in the beginning of RAM1. A copy is also stored in Flash and copied to RAM1 at startup. These functions are accessed by the Cortex-M7 ITCM bus, for the fastest possible performance. By default, functions without any memory type defined are treated as FASTRUN. A small amount of memory is typically unused, because the ITCM bus must access a memory region which is a multiple of 32K.
FLASHMEM - Functions defined with "FLASHMEM" executed directly from Flash. If the Cortex-M7 cache is not already holding a copy of the function, a delay results while the Flash memory is read into the M7's cache. FLASHMEM should be used on startup code and other functions where speed is not important.
...

ITCM is instruction portion of the 512 KB of RAM1 - the rest of that 512KB goes to DTCM of DATA.

The Code goes first and allocates in blocks of 32KB ... In the first padding was down to "padding:56" that is the end of the 32KB Code block before the DATA area can begin.

Adding that function required the next block - which exhausted the rest of RAM1 that was needed for DATA :: free for local variables:27968

Simple solution is to shrink back the CODE region using FLASHMEM like :: FLASHMEM void showMIDIControllerEditExpressionPedalsScreen(){

Do that to some functions and watch the change in : RAM1: variables:398016, code:98248, padding:56

Pick disused rare or large functions that won't hurt if they go slow when called. Though there is a 32KB cache for holding frequently used CODE stored in flash, if not much code passes through that cache then once loaded it will run at CPU speed until it may be swapped out.

Alternatively some BIG or data that can run more slowly ( again with a perhaps more widely shared 32KB DATA cache ) using the DMAMEM : DMAMEM const char elecInstrumentNames[10][6] = {"Mast", "eKick", "eSnar", "eClHH", "eOpHH", "eLTom", "eHTom", "Synt", "Uni", "Sub"};
 
That did it! It makes perfect sense now that you pointed it out.

Thank you so much for your quick and awesome advice. I will sprinkle FLASHMEM about a bit for functions that aren't time critical.
 
Glad to help. The promise of 1MB is a bit warped by necessary details of creation.

Post #2 was updated a bit with notes on DMAMEM ...
 
Thanks again. The DMAMEM note is useful too. The code I am adding at the moment is not time critical so I will make that FLASHMEM and a few others that are for "editing" the music or MIDI controller "Editing".

I was thinking the program was near to being "enough stuff". This has convinced me that I need to put a line under it.
 
@Paul - does the problem and p#2 give any idea to expand the T_4.x memory notes a bit - perhaps a note in the 'Programming" section that follows? Indicating how the problem shows - and an approach to resolve it with FLASHMEM/DMAMEM in context..

The shame of the memory map is that it doesn't show after there is a problem like it does before - in fact it looks like the before in p#1:
Code:
Memory Usage on Teensy 4.1:
  FLASH: code:101396, data:373592, headers:8336   free for files:7643140
   RAM1: variables:398016, code:98248, padding:56   free for local variables:27968
   RAM2: variables:15520  free for malloc/new:508768

While being SPARSE when it fails ... odd that the memory info indicated is out of place in the output ... mixed with LIBS used ???
Code:
Memory Usage on Teensy 4.1:
[B][COLOR="#FF0000"]Multiple libraries were found for "Audio.h"  [/COLOR][/B]FLASH: code:101460, data:373592, headers:8272   free for files:7643140


 Used: /Applications/Teensyduino.app/Contents/Java/hardware/teensy/avr/libraries/Audio
 Not used: /Users/jmw/Documents/Electronics/Arduino/libraries/Audio_-_Adafruit_Fork
Multiple libraries were found for "MIDI.h"
 Used: /Applications/Teensyduino.app/Contents/Java/hardware/teensy/avr/libraries/MIDI
 Not used: /Users/jmw/Documents/Electronics/Arduino/libraries/MIDI_Library
Multiple libraries were found for "SD.h"
  [B][COLOR="#FF0000"] RAM1: variables:398016, code:98312, padding:32760   free for local variables:-4800[/COLOR][/B]
 Used: /Applications/Teensyduino.app/Contents/Java/hardware/teensy/avr/libraries/SD
   [B][COLOR="#FF0000"]RAM2: variables:15520  free for malloc/new:508768
Error program exceeds memory space[/COLOR][/B]
 Not used: /Applications/Teensyduino.app/Contents/Java/libraries/SD
exit status 255
/Applications/Teensyduino.app/Contents/Java/arduino-builder returned 255
Error compiling for board Teensy 4.1.
 
perhaps a note in the 'Programming" section that follows? Indicating how the problem shows - and an approach to resolve it with FLASHMEM/DMAMEM in context..

Do you have a specific proposal?


odd that the memory info indicated is out of place in the output ... mixed with LIBS used ???

Yes, it is a bit odd. The Arduino IDE prints that after teensy_size runs. While it could be changed, I'm not eager to put even more patches into the Java code.

But teensy_size could be changed for 1.56.
 
Do you have a specific proposal?

Yes, it is a bit odd. The Arduino IDE prints that after teensy_size runs. While it could be changed, I'm not eager to put even more patches into the Java code.

But teensy_size could be changed for 1.56.

Just seeing this { responding to Is-it-possible-to-load-Teensy4-1-if-total-data-in-hex-file-exceeds-MAX_MEMORY_SIZE } ... 'proposal' was some variation of the notes and details made in above post #2

The Memory map and compile/link location names are factually complete - but under 'programming' some contextual 'english' restatement as post #2 above [ and #2 on linked 'MAX_MEMORY_SIZE' ] might clarify the needed mechanics of utilizing the available resources when the 1062 architecture results in a 'broken build'.
 
@Paul : something along these lines ... first guess ...

Using a T_4.x with 1062 MCU as shown in teensy41.html#memory the processor has 1MB of RAM.
That RAM has two equal sized halves of 512KB where one half is Full speed RAM1 for Instructions and Data. The second 512KB half is RAM2 available for runtime usage and runs at one quarter CPU speed.

When compiled Code and Data { 'global or static' data initialized or zeroed and stack local variables } exceed the 512KB of RAM1 the build will fail and code or data needs to be moved to Flash or allocated/used from RAM2 at runtime.
> this is done with const on DATA or the 'Static Allocation Keywords' indicated in the above Memory section.

Default RAM1 allocation Code goes first and allocates in blocks of 32KB. If that exceeds 512KB or leaves too little room for DTCM data:
> For CODE reduction the solution is to shrink back the ITCM allocated CODE region using FLASHMEM like :: FLASHMEM void myFunction(){ ...
-- Pick disused rare or large functions that won't hurt if they go slow when called. There is a 32KB CODE cache for holding frequently used CODE stored in flash, if not much code passes through that cache then once loaded it will run at CPU speed until it may be swapped out.

Alternatively some BIG data or data that can run more slowly ( again with a perhaps more widely shared 32KB DATA cache - used for ALL Data addressed outside RAM1's ITCM )
> using the flash stored data : const char myTableData[10][6] = {"Mast", "eKick", "eSnar", "eClHH", "eOpHH", "eLTom", "eHTom", "Synt", "Uni", "Sub"};
> or allocating dynamic memory with new/malloc() at runtime, or reserving it at compile time with DMAMEM and then having the code programmatically fill that data at runtime: DMAMEM char myTableData[10][6];
 
Last edited:
Status
Not open for further replies.
Back
Top