/* 8""8""8 8 8 8 8 8 8 e eeee eeeee eeeee 8 8 8 eeeee ee e eeee 8e 8 8 8 8 8 8 8 8 88 8e 8 8 8 8 88 8 8 88 8 8 8e 8e 8eee8e 8 8 88 8 8 8eee8 88 e8 8eee 88 8 8 88 88 88 8 8 8 88 8 8 88 8 8 8 88 88 8 8 88 88e8 88 8 8eee8 88ee8ee8 88 8 8ee8 88ee 8""""8 8 eeee eeeee e e eeee eeeee eeee eeee eeeee 8eeeee 8 8 8 8 8 8 8 8 8 8 8 8 8 88 8eee 8 8 8e 8 8eee 8e 8 8e 8eee 8eee8e e 88 88 8 __8 88 8 88 88 8 88 88 88 8 8eee88 88ee 8e888 88ee8 88ee 88 8 88e8 88ee 88 8 88 8 88 eeeeee eeee eeeee 88 8 8 8 8 8 8 8 88 e8 8 8 8 8 8eeee "8 8 8 8 8 8eeee 8 8 8 8888 8 8 8 8 8 8ee8 8888 88 8eeee8 8eee8 eeee8 eeeee eeeee ____ _ _ _ _ ____ _ _ ____ ____ ____ ___ ____ ____ |__| | | \_/ | | | | |__/ |__/ |__| | |___ [__ | | |___ |___ | |__| |__| | \ | \ | | | |___ ___] ____ ____ ____ ___ ____ _ ____ _ _ ____ ___ ____ _ _ ____ |__| |__/ |___ |__] |___ | | | |\ | | __ | | | | | [__ | | | \ |___ |__] |___ |___ |__| | \| |__] | |__| |__| ___] WaveSequencer V 1.0_6_5, all your rates are belong to us -------------------------------- This is a 'Keep It Low Level' project. Utilizing the hardware available and most common amongst tinkeres and DIY'ers. This is an open platform for every one to use, hack and adapt. */ #include #include #include #include const boolean DEBUG = true; const boolean showIntro = true; boolean megaMode = false; // Allow to play steps beyond maxCount that are not allocated in current preset boolean bpmMode = true; byte bpmDivider = 4; //1: Fourths bpm, 2: Eights bpm boolean technoMode = false; boolean confirmMetroChange = false; boolean stepButtonHoldToSet = false; Debug d = Debug(DEBUG, Serial); //void debugVar(char* title, long value); //void debugVarNoln(char* title, long value); //void debug(char* title); //void debug(int val); //void debugNoln(char* title); //void debugNoln(int val); // initialize the library with the numbers of the interface pins LiquidCrystal lcd(A5, 6, 7, 8, 9, A4); // These constants won't change. They're used to give names // to the pins used: const int stepButtonsPin = A0; // Analog input pin that the resistorladder is attached to const int funcButtonsPin = A1; const int motoraryPin = A2; // Analog input pin that the potentiometer is attached to const int metroPin = A3; byte maxSampleNum = 255; //185; //const int minRate = 1000; const unsigned int minRate = 1000; const unsigned int normalRate = 22050; const unsigned int maxRate = 44100; boolean newRate = true; const byte normalVolume = 0; unsigned int metroPotSplit = 250; // const unsigned int normalVol = ; const unsigned int holdFunctionTime = 1000; const unsigned int holdStepTime = 1500; unsigned int clock = 250; // msPerSec * secPerMin / BPM int externalClockTimeoutTime = 2000; boolean stepPlaybackEnabled = false; // enable playback of recorded steps? boolean recordClockSteps = true; //false; boolean playClockSteps = false; unsigned int clockChosen = clock; unsigned int bpm = (1000UL * 60) / bpmDivider / clock; boolean isPlaying = true; boolean jamMode = false; boolean randStep = false; byte randSeedSet = 0; unsigned int randSeedVal = 0; const byte fullSquareChar = B11111111; const byte squareChar = B11011011; const byte muteChar = B11101011; const byte underscoreChar = B01011111; boolean externalClock = false; boolean externalClockState = LOW; byte button = 0; byte lastButton = 255; byte buttonState = 0; boolean lastButtonState = 0; boolean stepButtonSet = 0; byte funcButton = 1; byte lastFuncButton = 0; byte funcButtonState = 0; byte lastFuncButtonState = 0; //byte *buttonStatePtr = &buttonState; byte funcButtonCount = 0; unsigned long funcButtonCountTime = 0; byte lastPressedFuncButton = 0; byte updateMotorary = false; int lastStepButtonReading = 255; int lastFuncButtonReading = 255; byte funcMenu = 1; byte lastFuncMenu = 0; boolean confirmButtonAction = false; boolean stepButtonRisingEdge = false; boolean stepButtonFallingEdge = false; boolean funcButtonRisingEdge = false; boolean funcButtonFallingEdge = false; #define BTNMUTE 0 #define BTNSAMPLE 1 #define BTNRATE 2 #define BTNMETRO 3 #define BTNCOUNT 4 #define BTNPRESET 5 #define FUNCJUMP 127 boolean jumpJumpMode = false; #define HOLDOFF 255 #define HOLDON 254 #define HOLDONON 253 byte holdingFunction = HOLDOFF; byte holdingFunctionDouble = HOLDOFF; boolean holdingFunctionEntering = false; byte holdingStep = HOLDOFF; boolean holdingStepEntering = false; byte copyAndPaste = HOLDOFF; boolean btnHeldWhenPresetLoaded = false; #define LCDUPDATED B10000000 #define LCDEXPIRED B01000000 #define TOPLINE B00000001 #define BOTTOMLINE B00000010 #define BOTHLINES B00000011 #define TOPDATA B00000100 byte lcdRefreshState = 0; byte lcdStepRisingState = 0; byte lcdRefreshPrep = 0; long lastMotaryVal = 0; int lastPotReading = 0; byte currentSlot = 255; // byte samples[16] = // { // 0,0,0,0, 0,0,0,0, // 0,0,0,0, 0,0,0,0 // }; // unsigned int rates[16] = // { // normalRate,normalRate,normalRate,normalRate, normalRate,normalRate,normalRate,normalRate, // normalRate,normalRate,normalRate,normalRate, normalRate,normalRate,normalRate,normalRate // }; // boolean mutedSamples[16] = // { // false,false,false,false, false,false,false,false, // false,false,false,false, false,false,false,false // }; // byte maxCount = 15; struct sequence { byte samples[16]; unsigned int rates[16]; //byte volumes[16]; boolean mutedSamples[16]; byte maxCount; }; // Struct size: 16 * ( 1 + 2 + 1) + 1 // Eeprom banks uses this size 16 times plus an index of 2 bytes // Each byte stored per step means 256 more bytes for the banks // A way to calculate it on the fly: // 2 + 16*16 * (1 + 2 + 1) + 16*1 = // 2 + 256 * 4 + 16 * 1 = // 2 + 1024 + 16 = 1042; // We need compress at least 18 bytes away. // The 256 bytes for mutedSamples can be 2 bytes per bank, // 32 in total, and with the new calculation: // 2 + 16*16 * (1 + 2) + 16*(1 + 2) = // 2 + 256 * 3 + 16 * 3 = // 2 + 768 + 48 = 818 // This leaves 1024-818 = 206 bytes unused struct sequence seq = { { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }, { normalRate,normalRate,normalRate,normalRate, normalRate,normalRate,normalRate,normalRate, normalRate,normalRate,normalRate,normalRate, normalRate,normalRate,normalRate,normalRate }, // { // normalVolume,normalVolume,normalVolume,normalVolume, normalVolume,normalVolume,normalVolume,normalVolume, // normalVolume,normalVolume,normalVolume,normalVolume, normalVolume,normalVolume,normalVolume,normalVolume // }, { false,false,false,false, false,false,false,false, false,false,false,false, false,false,false,false }, 15 }; byte* samples = seq.samples; unsigned int* rates = seq.rates; // byte* volumes = seq.volumes; boolean* mutedSamples = seq.mutedSamples; byte maxCount = seq.maxCount; byte volumes[16] = { normalVolume,normalVolume,normalVolume,normalVolume, normalVolume,normalVolume,normalVolume,normalVolume, normalVolume,normalVolume,normalVolume,normalVolume, normalVolume,normalVolume,normalVolume,normalVolume }; boolean lockedSteps[16] = { false,false,false,false, false,false,false,false, false,false,false,false, false,false,false,false }; unsigned int clockSteps[16] = { clock,clock,clock,clock, clock,clock,clock,clock, clock,clock,clock,clock, clock,clock,clock,clock }; struct presetIndex { unsigned int index; }; struct presetIndex presetIndx = { 0 }; unsigned int presetIndex = presetIndx.index; byte currentPreset = 255; byte seqStep = maxCount; byte stepPlaying = maxCount; byte lastStep = maxCount - 1; unsigned long stepCount = 0; boolean showCurStep = true; TimedAction metro = TimedAction(clock, tick); TimedAction rate = TimedAction(10, updateRate); TimedAction holdFunction = TimedAction(holdFunctionTime, functionHeld); TimedAction holdStep = TimedAction(holdStepTime, stepHeld); TimedAction motoraryInput = TimedAction(10, updateMotoraryInput); TimedAction metroPot = TimedAction(100 /*50*/, updateMetroPot); TimedAction blinkingStep = TimedAction(250, updateBlinkStepChar); TimedAction lcdRefresh = TimedAction(250, lcdRefreshingAction); TimedAction presetRow = TimedAction(500, lcdPresetRow); TimedAction externalClockTimeout = TimedAction(externalClockTimeoutTime, externalClockOff); unsigned int lastRate = 0; unsigned long loops = 0; unsigned long loopTime = 0; unsigned long loopTimes[10]; byte loopTimesIdx = 0; #define lcdString(x) lcdString_P(PSTR(x)); void lcdString_P(PGM_P str) { for (char c; (c = pgm_read_byte(str)); str++) lcd.write(c); } #define lcdStringDelay(x, ms) lcdString_P(PSTR(x), ms); void lcdString_P(PGM_P str, unsigned int ms) { for (char c; (c = pgm_read_byte(str)); str++) { lcd.write(c); delay(ms); } } #define SerialPrint(x) SerialPrint_P2(PSTR(x)) void SerialPrint_P2(PGM_P str) { for (uint8_t c; (c = pgm_read_byte(str)); str++) Serial.write(c); } /* SSS EEEE TTTTTT U U PPPP S E TT U U P P SSS EEE TT U U PPPP S E TT U U P SSSS EEEE TT UUU P */ void setup() { // initialize serial communications at 9600 bps: Serial.begin(9600); lcd.begin(16, 2); //digitalWrite(A0, HIGH); holdFunction.disable(); holdStep.disable(); blinkingStep.disable(); lcdRefresh.disable(); presetRow.disable(); motoraryInput.disable(); externalClockTimeout.disable(); // zeroPresetIndex(); loadPresetIndex(); if(showIntro) { lcd.clear(); lcd.setCursor(0, 0); lcdStringDelay("Micro", 150); delay(200); lcdStringDelay("Wave", 100); delay(300); lcdStringDelay("Sampler", 200); delay(1000); lcd.setCursor(0, 1); lcdStringDelay("Design: Dogenigt", 50); delay(1500); lcd.setCursor(0, 1); lcdString(" "); lcd.setCursor(0, 1); lcdStringDelay("Code+PCB: DimsOs", 50); delay(1500); } setupWave(); if(showIntro) delay(500); lcd.clear(); lcd.setCursor(0, 0); //lcdstring("System ready", 100, true); lcd.blink(); if(showIntro) delay(2000); if(showIntro) { lcdStringDelay("System not ready", 150); } maxSampleNum = numberOfSamples(maxSampleNum); if(showIntro) { for (int i = 15; i >= 9; i--) { lcd.setCursor(i, 0); delay(200); lcd.print(' '); } lcd.setCursor(9, 0); delay(400); lcdStringDelay("w ", 300); lcdStringDelay("rea", 150); lcdStringDelay("dy", 300); delay(1000); } lcd.setCursor(0,0); lcd.print("TOTAL FILES: "); lcdPrintNumber(maxSampleNum, 3, ' ', false); lcd.setCursor(0,1); delay(1000); lcd.print("22kHz 12bit MONO"); delay(1500); lcd.noBlink(); lcd.clear(); //Do first tings before first loop // samplCharMapTest(); // Make motorary ready for sample menu resetMotorary( 0, 0, maxSampleNum ); updateMetroPot(); randSeedVal = analogRead(metroPin); randomSeed(randSeedVal); lcd.setCursor(0, 0); lcdString("Select a sample "); if(DEBUG >= 2) { setRandomSamples(185); } lcdUpdateSampleRowNoCursor(); // don't print cursor seqStep = maxCount; tick(); loopTime = millis(); } /* RRRR AA TTTTTT EEEE TTTTTT III CCC K K R R A A TT E TT I C K K RRRR AAAA TT EEE TT I C KK R R A A TT E TT I C K K R RR A A TT EEEE ,, TT III CCC K K , */ void updateRate() { if (newRate || rates[stepPlaying] != lastRate) { waveRate(rates[stepPlaying]); lastRate = rates[stepPlaying]; newRate = false; } } void tick() { stepCount++; lastStep = seqStep; if (randStep) { byte randTries = 0; do { seqStep = random(0, maxCount + 1); randTries++; } while (mutedSamples[seqStep] && randTries < 100); } else { seqStep++; // === seqStep = seqStep + 1; if (seqStep > maxCount || (seqStep > 15 && !megaMode)) { seqStep = 0; } } if (stepPlaybackEnabled) { if(playClockSteps) { unsigned int nextClock = clockSteps[ (seqStep + 1) % maxCount ]; metro.setInterval(nextClock); clock = nextClock; } } //Only play samples that aren't muted and sample number not set to zero (aka zeroskip). if (!mutedSamples[seqStep]) { playStep(seqStep); } if ( (holdingFunction != BTNMUTE && funcButton != BTNPRESET) || btnHeldWhenPresetLoaded) { lcdUpdateSeqPosition(); } } void playStep(byte step) { if (samples[step] != 0) { stepPlaying = step; samplefinder(step); newRate = true; updateRate(); rate.reset(); //meh } }; /* L OOO OOO PPPP L O O O O P P L O O O O PPPP L O O O O P LLLL OOO OOO P */ void loop() { //find Wave file to load metro.check(); rate.check(); motoraryInput.check(); blinkingStep.check(); metroPot.check(); presetRow.check(); externalClockTimeout.check(); if (externalClock) { updateExternalClock( analogRead(metroPin) ); } button = readStepButtons(stepButtonsPin, &buttonState, &lastStepButtonReading, button); delay(1); funcButton = readStepButtons(funcButtonsPin, &funcButtonState, &lastFuncButtonReading, funcButton); delay(1); if (funcButton != lastPressedFuncButton) { lastPressedFuncButton = funcButton; updateMotorary = true; } stepButtonRisingEdge = (buttonState == 1 && lastButtonState == 0); stepButtonFallingEdge = (buttonState == 0 && lastButtonState == 1); funcButtonRisingEdge = (funcButtonState == 1 && lastFuncButtonState == 0); funcButtonFallingEdge = (funcButtonState == 0 && lastFuncButtonState == 1); //accept button input as active differently depending on whether the step buttons are hold-to-set if (stepButtonHoldToSet) { stepButtonSet = buttonState; } else { if (stepButtonRisingEdge) stepButtonSet = true; if (funcButtonRisingEdge) stepButtonSet = false; // eq. to stepButtonSet = (stepButtonSet || stepButtonRisingEdge) && !funcButtonRisingEdge } /* ____ _ _ _ _ ____ ____ _ ____ _ _ _ ____ |___ | | |\ | | |__/ | [__ | |\ | | __ | |__| | \| |___ | \ | ___] | | \| |__] */ unsigned long timeNow = millis(); if (funcButtonRisingEdge && timeNow - funcButtonCountTime > 100) { funcButtonCountTime = timeNow; if (copyAndPaste != HOLDOFF) { if (true || ( funcButton != BTNSAMPLE && funcButton != BTNRATE ) ) { copyAndPaste = HOLDOFF; lcdRefresh.setInterval(250); lcdRefreshPut(TOPLINE); } } // Count presses on the same funcbutton if (funcButton == lastFuncButton) { // if (millis() - funcButtonCountTime > 10) { funcButtonCount++; // } // lcdRefreshPut(lcdStepRisingState); // lcdStepRisingState = LCDUPDATED; } // New funcbutton pressed else { jumpJumpMode = false; funcButtonCount = 1; lcdRefreshPut(TOPLINE); // Disable jammode when changing function if (!isPlaying) { if (funcButton == BTNMUTE || funcButton == BTNCOUNT || funcButton == BTNPRESET || funcButton == FUNCJUMP ) { jamMode = false; } else { jamMode = true; } } switch (lastFuncButton) { case BTNMUTE: lcdUpdateSampleRowNoCursor(); break; case BTNPRESET: // Disable preset display and update samples chars when changing function from preset presetRow.disable(); lcdUpdateSampleRowNoCursor(); holdingFunctionDouble = HOLDOFF; confirmButtonAction = false; break; case BTNCOUNT: // No motorary input when changing function from max count motoraryInput.disable(); break; } } // Toggle double hold mode if already in holdingFunction in preset function if (funcButton == BTNPRESET && holdingFunction == BTNPRESET ) { if (holdingFunctionDouble == BTNPRESET) { holdingFunctionDouble = HOLDOFF; } else { holdingFunctionDouble = BTNPRESET; } } // Reset hold mode holdingFunction = HOLDOFF; // Monitor new hold time holdFunction.reset(); holdFunction.enable(); switch (funcButton) { case BTNMUTE: if (funcButtonCount == 3) { setRandomMutes(); lcdRefreshPut(BOTHLINES); } else { lcdRefreshPut(TOPLINE); } break; case BTNSAMPLE: if (funcButtonCount == 3) { // byte maxSampleNum = 35; setRandomSamples(maxSampleNum); lcdRefreshPut(BOTHLINES); } else { lcdRefreshPut(TOPLINE); } break; case BTNRATE: if (funcButtonCount == 3) { setRandomRates(); lcdRefreshPut(TOPLINE); } else { lcdRefreshPut(TOPLINE); } break; case BTNMETRO: // lcd.setCursor(0, 0); // lcdString(" : Metro "); // lcd.setCursor(10, 0); break; case BTNCOUNT: if (funcButtonCount == 3) { funcButton = FUNCJUMP; jumpJumpMode = true; lcdRefreshPut(TOPLINE); } else { if(jumpJumpMode) { jumpJumpMode = false; lcdRefreshPut(TOPLINE); } // Enable and update motorary input motoraryInput.enable(); motoraryInput.reset(); updateMotoraryInput(); } break; case BTNPRESET: // Just show the preset slots lcdPresetRow(); break; } /* if (funcButton == BTNCOUNT) { if (funcButtonCount == 3) { funcButton = FUNCJUMP; d.debug("FUNCJUMP x 3PLUS"); } } */ if (copyAndPaste != 255) { copyAndPaste = 255; lcdRefreshing(TOPLINE); copyAndPaste = button; lcdRefreshingPrep(TOPLINE, 1000); } } else if (funcButtonFallingEdge) { holdFunction.disable(); if (holdingFunction == HOLDOFF) { switch (funcButton) { case BTNPRESET: lcdRefreshPut(TOPLINE); holdingFunctionDouble = HOLDOFF; confirmButtonAction = false; break; case BTNMETRO: if (confirmMetroChange && clock != clockChosen) { clock = clockChosen; metro.setInterval(clock); lcd.setCursor(0,0); lcdString("TEMPO SET:"); if (bpmMode) { lcdPrintNumber(bpm, 4, ' ', false); lcdString("bm"); } else { lcdPrintNumber(clock, 4, ' ', false); lcdString("ms"); } lcdRefreshingPrep(TOPLINE, 1000); break; } // Change the metro on and off and update jam mode. if (isPlaying) { isPlaying = false; jamMode = true; if (!externalClock) metro.disable(); } else { isPlaying = true; jamMode = false; if (!externalClock) { metro.reset(); metro.enable(); } //Tick immediately, next tick when metro is triggered tick(); } lcdRefreshing(TOPLINE); break; } } } // END Func rising /* ____ ___ ____ ___ ____ _ ____ _ _ _ ____ [__ | |___ |__] |__/ | [__ | |\ | | __ ___] | |___ | | \ | ___] | | \| |__] */ if (stepButtonRisingEdge) { blinkStep(button); funcButtonCount = 0; d.debug("Step rising"); lcdRefreshPut(lcdStepRisingState); lcdStepRisingState = LCDUPDATED; // Monitor new step hold time if (funcButton == BTNSAMPLE || funcButton == BTNRATE) { holdStep.reset(); holdStep.enable(); } /* ____ _ _ _ _ ____ ____ ___ ____ ___ |___ | | |\ | | __ [__ | |___ |__] | |__| | \| |___ ___] | |___ | */ switch (funcButton) { case BTNMUTE: // lcdUpdateVolText(button); // if (stepButtonRisingEdge) { if (holdingFunction == BTNMUTE) { lockedSteps[button] = !lockedSteps[button]; lcdLockedRow(); } else { mutedSamples[button] = !mutedSamples[button]; lcdUpdateStepChar(button, true); lcdPrintStep(button, 0); } break; case BTNRATE: if (holdingFunctionEntering) { holdingFunctionEntering = false; setAllRates(rates[button]); lcdAllRates(button); } // no break! Do as if btn sample case BTNSAMPLE: if (copyAndPaste != HOLDOFF) { samples[button] = samples[copyAndPaste]; rates[button] = rates[copyAndPaste]; mutedSamples[button] = mutedSamples[copyAndPaste]; lockedSteps[button] = lockedSteps[copyAndPaste]; copyAndPaste = button; } // break both BTNRATE and BTNSAMPLE break; case BTNMETRO: if (holdingFunction == BTNMETRO) { /* lastStep = seqStep; seqStep = button - 1; //when tick the next step is played //Update LCD with normal stepChar, not cursor char. lcdUpdateStepChar(lastStep, true); //lcdUpdateSeqPosition will be called in tick() if (!isPlaying) { metro.reset(); metro.enable(); isPlaying = true; //Tick immediately, next tick when metro is triggered tick(); } */ } break; case BTNCOUNT: if(jumpJumpMode) break; maxCount = button; if (seqStep > maxCount) seqStep = maxCount; resetMotorary( maxCount, 0, 15 ); lcdUpdateSampleRow(); lcdUpdateCountText(maxCount); break; case BTNPRESET: // Delete preset function if (holdingFunction == BTNPRESET && holdingFunctionDouble == BTNPRESET) { lcd.setCursor(0, 0); lcdString("DELETE BANK?: "); lcdPrintStep(button, 14); blinkStep(button); if (confirmButtonAction) { if (!isSetPreset(button)) { // Cannot delete empty preset lcd.setCursor(0, 0); lcdString("NO PRESET: "); lcdPrintStep(button, 14); confirmButtonAction = false; } else if (button == lastButton) { deletePresetIndex(button); lcd.setCursor(0, 0); lcdString("BANK DELETED "); lcdPrintStep(button, 5); lcd.setCursor(button, 1); lcd.write(underscoreChar); confirmButtonAction = false; } } else { confirmButtonAction = true; } } // Save preset function else if (holdingFunction == BTNPRESET) { lcd.setCursor(0, 0); lcdString("SAVE PRESET? "); lcdPrintStep(button, 14); blinkStep(button); if (confirmButtonAction) { if (button == lastButton) { if (!isSetPreset(button)) { // Mark slot not empty lcd.setCursor(button, 1); lcd.write(fullSquareChar); } storePreset(button); lcd.setCursor(0, 0); lcdString("SAVED TO BANK "); lcdPrintStep(button, 14); confirmButtonAction = false; } } else { confirmButtonAction = true; } } // Load preset function else { boolean presetLoaded = loadPreset(button); if (seqStep > maxCount) { seqStep = maxCount; } if (presetLoaded) { lcdUpdateSampleRowNoCursor(); lcd.setCursor(0, 0); lcdString("PRESET LOADED"); lcdPrintStep(button, 7); btnHeldWhenPresetLoaded = true; } } break; /* // Extra funcButton menus case FUNCJUMP: d.debug("FUNCJUMP 4realzies"); // lastStep = seqStep; // seqStep = button - 1; //when tick the next step is played // //Update LCD with normal stepChar, not cursor char. // lcdUpdateStepChar(lastStep, true); // //lcdUpdateSeqPosition will be called in tick() // if (!isPlaying) { // metro.reset(); // metro.enable(); // isPlaying = true; // //Tick immediately, next tick when metro is triggered // tick(); // } break; */ } // END Func step if (jumpJumpMode) { lastStep = seqStep; seqStep = button - 1; //when tick the next step is played //Update LCD with normal stepChar, not cursor char. lcdUpdateStepChar(lastStep, true); //lcdUpdateSeqPosition will be called in tick() if (!isPlaying) { metro.reset(); metro.enable(); isPlaying = true; //Tick immediately, next tick when metro is triggered tick(); } } //When playmode i active, play the step if (jamMode) { playStep(button); } } else if (stepButtonFallingEdge) { stopBlinkStep(); holdStep.disable(); switch(funcButton) { case BTNPRESET: presetRow.reset(); presetRow.enable(); break; } } // END Step rising /* SSS TTTTTT EEEE PPPP BBBB U U TTTTTT TTTTTT OOO N N S TT E P P B B U U TT TT O O NN N SSS TT EEE PPPP BBBB U U TT TT O O N N N S TT E P B B U U TT TT O O N NN SSSS TT EEEE P BBBB UUU TT TT OOO N N */ if (stepButtonSet) { //React to last pressed funcButton switch (funcButton) { //Step mute ( on/off ) case BTNMUTE: break; //Sample number case BTNSAMPLE: updateSampleNum(button); if (stepButtonRisingEdge) { lcdUpdateSampleText(button); } break; //Sample rate case BTNRATE: updateSampleRate(button); if (stepButtonRisingEdge) { lcdUpdateRateText(button); } break; case BTNMETRO: break; case BTNCOUNT: break; case BTNPRESET: break; } // wait 2 milliseconds before the next loop // for the analog-to-digital converter to settle // after the last reading: delay(1); } /* L CCC DDD EEEE X X PPPP III RRRR EEEE DDD L C D D E X X P P I R R E D D L C D D EEE X PPPP I RRRR EEE D D L C D D E X X P I R R E D D LLLL CCC DDD EEEE X X P III R RR EEEE DDD */ lcdRefreshing(lcdRefreshState); /* N N EEEE X X TTTTTT L OOO OOO PPPP NN N E X X TT L O O O O P P --- N N N EEE X TT L O O O O PPPP N NN E X X TT L O O O O P N N EEEE X X TT LLLL OOO OOO P */ lcdRefresh.check(); holdFunction.check(); holdStep.check(); if (funcButton == BTNMETRO) { // if (lastFuncButton != BTNPRESET) { // funcButton = lastFuncButton; // } } lastButton = button; lastFuncButton = funcButton; lastFuncButtonState = funcButtonState; lastButtonState = buttonState; if (funcButtonCount == 3) { funcButtonCount = 0; } // Benchmarking loop time // unsigned long timeNow = millis(); if (DEBUG >= 2) { unsigned long loopInterval = timeNow - loopTime; if (loops > 10) { loopInterval = (loopInterval + 9*loopTimes[loopTimesIdx])/10; } loopTimesIdx = (loopTimesIdx + 1) % 10; loopTimes[loopTimesIdx] = loopInterval; if (loopTimesIdx == 0) { if ( loopInterval < 100) { // lcd.setCursor(6, 0); // lcd.print(loopInterval); d.debugVar("lop ", loopInterval); } else { // lcd.setCursor(4, 0); // lcdString("long loop:"); // lcd.print(loopInterval); d.debugVar("long loop ", loopInterval); } } } loopTime = timeNow; loops++; // End Benchmarking loop time } /* FFFF U U N N CCC TTTTTT III OOO N N H H EEEE L DDD F U U NN N C TT I O O NN N H H E L D D FFF U U N N N C TT I O O N N N HHHH EEE L D D F U U N NN C TT I O O N NN H H E L D D F UUU N N CCC TT III OOO N N H H EEEE LLLL DDD */ void functionHeld() { holdingFunction = funcButton; //Set hold mode to the button equal to the holdingFunctionEntering = true; if (holdingFunction == BTNPRESET) { confirmButtonAction = false; } // Activate timer for motorary input switch (funcButton) { case BTNMUTE: case BTNSAMPLE: case BTNRATE: motoraryInput.enable(); motoraryInput.reset(); updateMotoraryInput(); break; } switch (funcButton) { case BTNMUTE: // lcdUpdateVolText(button); lcdRefreshPut(TOPLINE); lcdLockedRow(); break; case BTNSAMPLE: lcdAllSamples( samples[button] ); resetMotorary( samples[button], 0, maxSampleNum ); break; case BTNRATE: lcdAllRates(button); resetMotorary( rates[button], minRate, maxRate ); break; case BTNMETRO: //holdingFunction is set lcdRefreshPut(TOPLINE); break; case BTNCOUNT: // //megaMode = !megaMode; // technoMode = !technoMode; // lcd.setCursor(0, 0); // lcdString("TECHNO mode "); // // if (megaMode) { // if (technoMode) { // lcdString("ON "); // } else { // lcdString("off "); // } // lcdRefreshingPrep(TOPLINE, 2000); randStep = !randStep; lcdRefreshPut(TOPLINE); break; case BTNPRESET: lcdPresetRow(); lcdRefreshPut(TOPLINE); break; } holdFunction.disable(); } /* SSS TTTTTT EEEE PPPP H H EEEE L DDD S TT E P P H H E L D D SSS TT EEE PPPP HHHH EEE L D D S TT E P H H E L D D SSSS TT EEEE P H H EEEE LLLL DDD */ void stepHeld() { holdingStep = button; //Set hold mode to the button equal to the holdingStepEntering = true; // Activate timer for motorary input /* switch (funcButton) { case BTNMUTE: case BTNSAMPLE: case BTNRATE: motoraryInput.enable(); motoraryInput.reset(); updateMotoraryInput(); break; } */ switch (funcButton) { case BTNMUTE: break; case BTNSAMPLE: case BTNRATE: copyAndPaste = button; lcdRefreshing(TOPLINE); break; case BTNMETRO: break; case BTNCOUNT: break; case BTNPRESET: break; } holdStep.disable(); } /* ____ ____ ___ ____ _ _ [__ |___ | |__| | | ___] |___ | | | |___ |___ ... */ void setAllSamples(byte sampleNum) { for (int i = 0; i < 16; i++) { samples[i] = sampleNum; } } void setAllRates(unsigned int rate) { for (int i = 0; i < 16; i++) { rates[i] = rate; } } void setAllMutes(boolean mute) { for (int i = 0; i < 16; i++) { mutedSamples[i] = mute; } } void setRandomSamples(byte maxSampleNum) { for (int i = 0; i < 16; i++) { if ( !lockedSteps[i] ) { samples[i] = random(1, maxSampleNum + 1U); } } } void setRandomRates() { for (int i = 0; i < 16; i++) { if ( !lockedSteps[i] ) { rates[i] = random(minRate, maxRate + 1); } } } void setRandomMutes() { for (int i = 0; i < 16; i++) { if ( !lockedSteps[i] ) { mutedSamples[i] = random(0, 2); } } } /* _ _ ___ ___ ____ ___ ____ _ _ _ _ _ _ | | |__] | \ |__| | |___ |\ | | | |\/| |__| | |__/ | | | |___ ... | \| |__| | | */ void updateSampleNum(byte button) { lastMotaryVal = samples[button]; // Check if step or function has changed, then set motorary posistion and range if (updateMotorary || button != lastButton) { //d.debugVar("rstMtry", lastFuncButton); resetMotorary( lastMotaryVal, 0, maxSampleNum ); updateMotorary = false; } int divisor = 3000; int motaryVal = readMotorary(divisor); //Check if motorary has changed if (motaryVal != lastMotaryVal) { //d.debugVar("newMtyVal", motaryVal); if (motaryVal >= 0 && motaryVal <= maxSampleNum) { samples[button] = motaryVal; //Store new motaryVal to button if (holdingFunction == BTNSAMPLE) { setAllSamples(motaryVal); lcdAllSamples(motaryVal); } if (holdingFunction == HOLDOFF) { lcdUpdateSampleText(button); if (DEBUG >= 3) printArray(samples, 16); } } } } void updateSampleRate(byte button) { lastMotaryVal = rates[button]; // Check if step or function has changed, then set motorary posistion and range if (updateMotorary || button != lastButton) { //d.debugVar("rstMtry", lastFuncButton); resetMotorary( lastMotaryVal, minRate, maxRate ); updateMotorary = false; } int divisor = 1; long motaryVal = readMotorary(divisor); // Check if motorary has changed if (motaryVal != lastMotaryVal) { // rates[button] = constrain( motaryVal, minRate, maxRate); //Store new motaryVal to button if (motaryVal >= minRate && motaryVal <= maxRate) { rates[button] = motaryVal; //Store new motaryVal to button if (holdingFunction == BTNRATE) { holdingFunctionEntering = false; setAllRates(rates[button]); lcdAllRates(button); } if (holdingFunction == HOLDOFF) { lcdUpdateRateText(button); if (DEBUG >= 3) printArrayUint(rates, 16); } } } } void updateMaxCount() { lastMotaryVal = maxCount; int divisor = 3000; int motaryVal = readMotorary(divisor); //Check if motorary has changed if (motaryVal != lastMotaryVal) { byte maxCountMax; if (megaMode) { maxCountMax = 255; } else { maxCountMax = 15; }; // Check if step or function has changed, then set motorary posistion and range if (lastFuncButton != BTNCOUNT) { resetMotorary( lastMotaryVal, 0, maxCountMax ); } if (motaryVal >= 0 && motaryVal <= maxCountMax) { maxCount = motaryVal; //Store new motaryVal to button // if (motaryVal != 0 && lastMotaryVal == 0) { // mutedSamples[button] = false; // } if (!megaMode) maxCount = constrain(maxCount, 0, 15); if (seqStep > maxCount) seqStep = maxCount; lcdUpdateSampleRow(); lcdUpdateCountText(maxCount); } } } void updateVolume(byte button) { lastMotaryVal = constrain(11 - volumes[button], 0 , 11); // 11 - volumes[button]; int divisor = 4000; int motaryVal = readMotorary(divisor); //Check if motorary has changed if (motaryVal != lastMotaryVal) { // Check if step or function has changed, then set motorary posistion and range if (lastFuncButton != BTNMUTE || button != lastButton) { resetMotorary( lastMotaryVal, 0, 11 ); } if (motaryVal >= 0 && motaryVal <= 11) { volumes[button] = 11 - motaryVal; //Store new motaryVal to button setVolume(volumes[button]); lcdUpdateVolText(button); } } } /* III N N PPPP U U TTTTTT SSS I NN N P P U U TT S I N N N PPPP U U TT SSS I N NN P U U TT S III N N P UUU TT SSSS */ byte readStepButtons(int buttonsPin, byte *buttonStatePtr, int *lastButtonReading, byte curButton) { // read the analog in value: int sensorValue = analogRead(buttonsPin); byte buttonNum = 255; if (abs(*lastButtonReading - sensorValue) <= 16) { // map it to the range of the analog out: buttonNum = (sensorValue + 15)/32; if (buttonNum <= 15) { *buttonStatePtr = 1; } else { *buttonStatePtr = 0; buttonNum = curButton; } if (DEBUG >= 3) { // Only print if debug is active /* static byte lastButtonDebug; // if (buttonNum != lastButton && buttonNum != 32) { // Serial.print(buttonNum); // Serial.print("\t "); // Serial.println(sensorValue); // } lastButtonDebug = buttonNum; */ } } else { buttonNum = curButton; } *lastButtonReading = sensorValue; return buttonNum; } const int baseVal = 511; const int speedThreshold = 2; long position = 0; long motoraryStartValue = 0; long motoraryMinValue = 0; long motoraryMaxValue = 0; //int lastPosition = 0; long readMotorary(int stepDivision) { // read the analog in value: int sensorValue = analogRead(motoraryPin); int speed = sensorValue - baseVal; if (abs(speed) > speedThreshold) { unsigned long speedFact = (speed*speed - speedThreshold*speedThreshold); if ( speed > 0 ) { position += speedFact; } else { position -= speedFact; } position = constrain(position, (motoraryMinValue - motoraryStartValue - 5) * (long) stepDivision, (motoraryMaxValue - motoraryStartValue + 5) * (long) stepDivision); funcButtonCount = 0; if (randSeedSet < 10) { // randSeedVal += speed; // randSeedVal /= 2; // randSeedVal = (randSeedVal + speed) / 2; randSeedVal = randSeedVal + speed; randomSeed(randSeedVal); randSeedSet++; } } // if ( abs(lastPosition - position) > 0 ) { // //motoren er drejet // lastPosition = position; // } //int stepDivision = 1000; long step = (motoraryStartValue + (position) / stepDivision ); return step; } // Husk at nulstille position imellem forskellige funktioner til motorary void resetMotorary(long startValue, long minValue, long maxValue) { position = 0; motoraryStartValue = startValue; motoraryMinValue = minValue; motoraryMaxValue = maxValue; } void updateMotoraryInput() { //if (stepButtonSet) switch(funcButton) { case BTNMUTE: if (holdingFunction == BTNMUTE) { // updateVolume(button); } break; case BTNSAMPLE: updateSampleNum(button); break; case BTNRATE: updateSampleRate(button); break; case BTNMETRO: break; case BTNCOUNT: updateMaxCount(); break; case BTNPRESET: break; } } void updateExternalClock(int reading) { int readState = LOW; if (reading < 256) readState = LOW; else if (reading >= 409) readState = HIGH; if(readState != externalClockState) { if(readState == HIGH) { // update clock static unsigned long lastExternalClockTime; unsigned long now = millis(); clock = now - lastExternalClockTime; lastExternalClockTime = now; // update tick if (isPlaying) { tick(); } if (recordClockSteps) { clockSteps[seqStep] = clock; } } else { } externalClockState = readState; } } void externalClockOff() { externalClock = false; externalClockTimeout.disable(); if (isPlaying) { metro.enable(); } playClockSteps = true; //w00t recordClockSteps = false; // Force reading metro value lastPotReading = 0; } void updateMetroPot() { // static int lastPotReading; int reading = analogRead(metroPin); if ( abs(reading - lastPotReading) < 3) return; unsigned int curClock = clock; if ( reading <= 10 ) { if (!externalClock) { metro.disable(); externalClock = true; recordClockSteps = true; playClockSteps = false; lcd.setCursor(0, 0); lcdString("EXT CLOCK ON "); lcdRefreshingPrep(TOPLINE, 2000); } externalClockTimeout.reset(); externalClockTimeout.enable(); } // else if (externalClock) { // reading > 10 // externalClock = false; // externalClockTimeout.disable(); // if (isPlaying) { // metro.enable(); // } // } if (externalClock) { updateExternalClock(reading); } else if (technoMode) { byte noSteps = 11; static unsigned int lastBpm; bpm = map(reading, 0, 1023, 0, 11); bpm = map(bpm, 0, 11, 60, 280); // 60-280, 30-140, 3-14: 12 if (bpm == lastBpm) return; clock = (1000UL * 60) / bpmDivider / bpm; } else { if (reading <= 420) clock = map( reading, 11, 420, 2000, metroPotSplit); else clock = map( reading, 421, 1023, metroPotSplit, 10); } if (!externalClock) { lcd.setCursor(0, 0); if (bpmMode) { bpm = (1000UL * 60) / bpmDivider / clock; lcdString("T:"); lcdPrintNumber(clock, 4, ' ', false); lcdString("ms "); lcdPrintNumber(bpm, 4, ' ', false); lcdString("bpm"); } else { lcdString("TEMPO: "); lcd.print(clock); lcdString("ms "); } lcdRefreshingPrep(TOPLINE, 2000); } if (confirmMetroChange) { clockChosen = clock; clock = curClock; } // else { metro.setInterval(clock); // } if (DEBUG >= 2) { lcd.setCursor(2, 0); lcd.print(';'); } lastPotReading = reading; //clock = 1000*60/newBpm; // clock = msPerSec * secPerMin / BPM // bpm = clock / (msPerSec * secPerMin) funcButtonCount = 0; }