diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ecda9df --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +software/.DS_Store +.DS_Store diff --git a/README.MD b/README.MD index d508d0f..58453ad 100644 --- a/README.MD +++ b/README.MD @@ -1,8 +1,17 @@ +Features: +- 6 output channels +- Master BPM +- Separate divider per chennel + TODO: +- Add multipliers - Add min and max values - External clock - Switch to U8G2 for screen - Save state to EEPROM when stopped +- swing +- long-press encoder for settings (input mode, pulse length) +- analogue input for modulations Timer library available here https://github.com/PaulStoffregen/FlexiTimer2 diff --git a/software/GToE/GToE.ino b/software/GToE/GToE.ino index 1e8796d..cbbc787 100644 --- a/software/GToE/GToE.ino +++ b/software/GToE/GToE.ino @@ -7,30 +7,35 @@ #define SCREEN_ADDRESS 0x3C +#define PPQN 24 //24 is good for triplets. each pulse is 1/512th note +#define PULSE_LENGTH 5 + #define INPUT_CONNECTED_PIN 12 #define INPUT_PIN 2 #define ENC_BTN_PIN 17 #define ENC_D1_PIN 4 #define ENC_D2_PIN 3 #define START_STOP_BTN_PIN 14 -//Output pins are defined in the array below int outsPins[6] = {5, 6, 7, 8, 9, 10}; -int outsValues[6] = {1, 2, 4, -4, 1, 1}; //positive - divide, negative - multiply, 0 - off + +int outsValues[6] = {1, 2, 4, -4, 0, 0}; //positive - divide, negative - multiply, 0 - off /2 /3 /4 /5 /6 /7 /8 /16 /32 /random x2 x3 x4 x6 x8 x12 x16 x24 int outsPeriods[6]; -int outsClocksCounts[6] = {0, 0, 0, 0, 0, 0}; +int outsClocksCounts[6]; bool clockMode; bool clockModeOld; int inputMode; //clock, reset, start/stop int clockCount = 0; -int bpm = 120; -int bpmPeriod; -int bpmClockCount; +int bpm = 127; +int pulseClockCount = 0; +int pulseCount = 0; +int pulsePeriod; bool isPlaying = 0; int needToResetChannel; +bool pulseCounted = false; int displayTab = 0; int displayTabOld; @@ -71,40 +76,51 @@ void loop() { void internalClock() { if (isPlaying) { - for (int i = 0; i<6; i++) { - if (outsClocksCounts[i] >= outsPeriods[i]) { - outsClocksCounts[i] = 0; + + // pulse rules for dividers (multipliers should be in separate for loop, as this one occurs only on pulse 0 (bpm)) + if (pulseCount == 0 && !pulseCounted) { + for (int i = 0; i<6; i++) { + if (outsValues[i] > 0) { //Dividers (>0) + if (outsClocksCounts[i] == 0) { //Pulse on 0 + digitalWrite(outsPins[i], HIGH); + } + if (outsClocksCounts[i] < (outsValues[i]-1)) { + outsClocksCounts[i]++; + if (i == 0) {Serial.println(outsClocksCounts[i]);} + } else { + outsClocksCounts[i] = 0; + } + } } - if (outsClocksCounts[i] == 0) { - digitalWrite(outsPins[i], HIGH); - } - if (outsClocksCounts[i] == 15) { //15ms pulse + pulseCounted = true; + } + + //lets get internal pulse going + if (pulseClockCount == 0) { + pulseCount++; + pulseCounted = false; + } + if (pulseClockCount < pulsePeriod) { + pulseClockCount++; + } else { + pulseClockCount = 0; + } + if (pulseCount >= PPQN) { + pulseCount = 0; + } + + // pull low all outputs after set pulse length + if (pulseClockCount >= PULSE_LENGTH) { + for (int i = 0; i<6; i++) { digitalWrite(outsPins[i], LOW); } - outsClocksCounts[i]++; - } - if (bpmClockCount < bpmPeriod) { - bpmClockCount++; - } else { - bpmClockCount = 0; - if (needToResetChannel>=0) { - outsClocksCounts[needToResetChannel] = 0; - updatePeriods(); - needToResetChannel = -1; - } } + } } void updatePeriods() { - bpmPeriod = 60000 / bpm; - for (int i = 0; i<6; i++) { - if (outsValues[i]>0) { - outsPeriods[i] = bpmPeriod * outsValues[i]; - } else { - outsPeriods[i] = bpmPeriod / outsValues[i] * - 1; - } - } + pulsePeriod = 60000 / (bpm * PPQN); } void resetClocks() { @@ -114,16 +130,18 @@ void resetClocks() { } void checkInputs() { + + //input jack switcch clockMode = digitalRead(INPUT_CONNECTED_PIN); if (clockMode != clockModeOld) { updateScreen(); clockModeOld = clockMode; } + + //encoder button if (needToChangeTab == 0 && digitalRead(ENC_BTN_PIN) == 0) { needToChangeTab = 1; - } - - if (needToChangeTab == 1 && digitalRead(ENC_BTN_PIN) == 1) { + } else if (needToChangeTab == 1 && digitalRead(ENC_BTN_PIN) == 1) { displayTabOld = displayTab; displayTab++; if (displayTab>6) { @@ -133,6 +151,7 @@ void checkInputs() { updateScreen(); } + //encoder encoder.tick(); int encPosition = encoder.getPosition(); if (encPositionOld != encPosition) { @@ -147,6 +166,7 @@ void checkInputs() { encPositionOld = encPosition; } + //play button if (!digitalRead(START_STOP_BTN_PIN) && !buttonPushed) { isPlaying = !isPlaying; resetClocks(); @@ -163,7 +183,7 @@ void updateScreen() { display.setCursor(0,0); display.setTextSize(1); if (displayTab == 0) { - display.setTextColor(SSD1306_BLACK, SSD1306_WHITE);resetClocks(); + display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); display.print(F(" ")); display.setTextColor(SSD1306_WHITE); display.print(F(" bpm ")); @@ -186,26 +206,26 @@ void updateScreen() { display.print(i); } } - display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); display.println(F(" ")); - display.println(); //Content display.setTextSize(3); display.setTextColor(SSD1306_WHITE); - if (displayTab == 0) { display.print(bpm); display.println(F("bpm")); } else { - if (outsValues[displayTab-1]>0) { + if (outsValues[displayTab-1]==0) { + display.print(F("OFF")); + } else if (outsValues[displayTab-1]>0) { display.print(F("/")); + display.print(abs(outsValues[displayTab-1])); } else { - display.print(F("x")); + display.print(F("x")); + display.print(abs(outsValues[displayTab-1])); } - display.print(abs(outsValues[displayTab-1])); } display.display();