Expensive Notes
Well-known member
I have a very large program which produces the following complier information:
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:
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
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:
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
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);
}