Midi clock and tempo
This commit is contained in:
parent
98fc15cef2
commit
5f7f241e82
@ -52,16 +52,17 @@ enum UIState {
|
|||||||
UI_MENU_MAIN,
|
UI_MENU_MAIN,
|
||||||
UI_MENU_RANDOMIZE,
|
UI_MENU_RANDOMIZE,
|
||||||
UI_MENU_SETUP,
|
UI_MENU_SETUP,
|
||||||
UI_SETUP_CHANNEL_EDIT
|
UI_SETUP_CHANNEL_EDIT,
|
||||||
|
UI_SETUP_TEMPO_EDIT
|
||||||
};
|
};
|
||||||
|
|
||||||
UIState currentState = UI_TRACKER;
|
UIState currentState = UI_MENU_MAIN;
|
||||||
|
|
||||||
const char* mainMenu[] = { "Tracker", "Randomize", "Setup" };
|
const char* mainMenu[] = { "Tracker", "Randomize", "Setup" };
|
||||||
const int mainMenuCount = sizeof(mainMenu) / sizeof(char*);
|
const int mainMenuCount = sizeof(mainMenu) / sizeof(char*);
|
||||||
const char* randomizeMenu[] = { "Back", "Scale", "Melody", "Theme 1", "Theme 2", "Theme 3", "Theme 4", "Theme 5", "Theme 6", "Theme 7" };
|
const char* randomizeMenu[] = { "Back", "Scale", "Melody", "Theme 1", "Theme 2", "Theme 3", "Theme 4", "Theme 5", "Theme 6", "Theme 7" };
|
||||||
const int randomizeMenuCount = sizeof(randomizeMenu) / sizeof(char*);
|
const int randomizeMenuCount = sizeof(randomizeMenu) / sizeof(char*);
|
||||||
const char* setupMenu[] = { "Back", "Channel", "Save", "Load" };
|
const char* setupMenu[] = { "Back", "Channel", "Tempo", "Save", "Load" };
|
||||||
const int setupMenuCount = sizeof(setupMenu) / sizeof(char*);
|
const int setupMenuCount = sizeof(setupMenu) / sizeof(char*);
|
||||||
|
|
||||||
int menuSelection = 0;
|
int menuSelection = 0;
|
||||||
@ -77,8 +78,9 @@ const uint32_t EEPROM_MAGIC = 0x42424244;
|
|||||||
volatile bool isEditing = false;
|
volatile bool isEditing = false;
|
||||||
volatile int scrollOffset = 0;
|
volatile int scrollOffset = 0;
|
||||||
volatile bool isPlaying = false;
|
volatile bool isPlaying = false;
|
||||||
unsigned long lastStepTime = 0;
|
|
||||||
volatile int tempo = 120; // BPM
|
volatile int tempo = 120; // BPM
|
||||||
|
volatile unsigned long lastClockTime = 0;
|
||||||
|
volatile int clockCount = 0;
|
||||||
|
|
||||||
|
|
||||||
// Encoder State
|
// Encoder State
|
||||||
@ -238,6 +240,12 @@ void sendMidi(uint8_t status, uint8_t note, uint8_t velocity) {
|
|||||||
Serial1.write(velocity);
|
Serial1.write(velocity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sendMidiRealtime(uint8_t status) {
|
||||||
|
mutex_enter_blocking(&midiMutex);
|
||||||
|
Serial1.write(status);
|
||||||
|
mutex_exit(&midiMutex);
|
||||||
|
}
|
||||||
|
|
||||||
void generateRandomScale() {
|
void generateRandomScale() {
|
||||||
numScaleNotes = random(3, 13); // 3 to 12 notes
|
numScaleNotes = random(3, 13); // 3 to 12 notes
|
||||||
for (int i = 0; i < 12; i++) {
|
for (int i = 0; i < 12; i++) {
|
||||||
@ -325,6 +333,11 @@ void handleInput() {
|
|||||||
if (midiChannel < 1) midiChannel = 16;
|
if (midiChannel < 1) midiChannel = 16;
|
||||||
if (midiChannel > 16) midiChannel = 1;
|
if (midiChannel > 16) midiChannel = 1;
|
||||||
break;
|
break;
|
||||||
|
case UI_SETUP_TEMPO_EDIT:
|
||||||
|
tempo += delta;
|
||||||
|
if (tempo < 40) tempo = 40;
|
||||||
|
if (tempo > 240) tempo = 240;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -379,11 +392,12 @@ void handleInput() {
|
|||||||
case UI_MENU_SETUP:
|
case UI_MENU_SETUP:
|
||||||
if (menuSelection == 0) { currentState = UI_MENU_MAIN; menuSelection = 2; }
|
if (menuSelection == 0) { currentState = UI_MENU_MAIN; menuSelection = 2; }
|
||||||
if (menuSelection == 1) currentState = UI_SETUP_CHANNEL_EDIT;
|
if (menuSelection == 1) currentState = UI_SETUP_CHANNEL_EDIT;
|
||||||
if (menuSelection == 2) {
|
if (menuSelection == 2) currentState = UI_SETUP_TEMPO_EDIT;
|
||||||
|
if (menuSelection == 3) {
|
||||||
saveSequence();
|
saveSequence();
|
||||||
currentState = UI_MENU_MAIN;
|
currentState = UI_MENU_MAIN;
|
||||||
}
|
}
|
||||||
if (menuSelection == 3) {
|
if (menuSelection == 4) {
|
||||||
if (loadSequence()) showMessage("LOADED!");
|
if (loadSequence()) showMessage("LOADED!");
|
||||||
currentState = UI_MENU_MAIN;
|
currentState = UI_MENU_MAIN;
|
||||||
}
|
}
|
||||||
@ -391,6 +405,9 @@ void handleInput() {
|
|||||||
case UI_SETUP_CHANNEL_EDIT:
|
case UI_SETUP_CHANNEL_EDIT:
|
||||||
currentState = UI_MENU_SETUP;
|
currentState = UI_MENU_SETUP;
|
||||||
break;
|
break;
|
||||||
|
case UI_SETUP_TEMPO_EDIT:
|
||||||
|
currentState = UI_MENU_SETUP;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -405,10 +422,13 @@ void handleInput() {
|
|||||||
Serial.print(F("Playback: ")); Serial.println(isPlaying ? F("ON") : F("OFF"));
|
Serial.print(F("Playback: ")); Serial.println(isPlaying ? F("ON") : F("OFF"));
|
||||||
if (isPlaying) {
|
if (isPlaying) {
|
||||||
playbackStep = navigationSelection > 0 ? navigationSelection - 1 : 0;
|
playbackStep = navigationSelection > 0 ? navigationSelection - 1 : 0;
|
||||||
lastStepTime = millis(); // Reset timer to start immediately
|
clockCount = 0;
|
||||||
|
lastClockTime = micros();
|
||||||
|
sendMidiRealtime(0xFA); // MIDI Start
|
||||||
} else {
|
} else {
|
||||||
// Send All Notes Off on stop (CC 123)
|
// Send All Notes Off on stop (CC 123)
|
||||||
needsPanic = true;
|
needsPanic = true;
|
||||||
|
sendMidiRealtime(0xFC); // MIDI Stop
|
||||||
queuedTheme = -1;
|
queuedTheme = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -420,9 +440,17 @@ void handleInput() {
|
|||||||
void handlePlayback() {
|
void handlePlayback() {
|
||||||
if (!isPlaying) return;
|
if (!isPlaying) return;
|
||||||
|
|
||||||
unsigned long interval = 15000 / tempo; // 16th notes (60000 / tempo / 4)
|
unsigned long currentMicros = micros();
|
||||||
if (millis() - lastStepTime > interval) {
|
unsigned long clockInterval = 2500000 / tempo; // 60s * 1000000us / (tempo * 24ppqn)
|
||||||
lastStepTime = millis();
|
|
||||||
|
if (currentMicros - lastClockTime >= clockInterval) {
|
||||||
|
lastClockTime += clockInterval;
|
||||||
|
|
||||||
|
sendMidiRealtime(0xF8); // MIDI Clock
|
||||||
|
|
||||||
|
clockCount++;
|
||||||
|
if (clockCount < 6) return; // 24 ppqn / 4 = 6 pulses per 16th note
|
||||||
|
clockCount = 0;
|
||||||
|
|
||||||
mutex_enter_blocking(&midiMutex);
|
mutex_enter_blocking(&midiMutex);
|
||||||
|
|
||||||
@ -492,6 +520,11 @@ void drawMenu(const char* title, const char* items[], int count, int selection)
|
|||||||
display.print(F(": "));
|
display.print(F(": "));
|
||||||
display.print(midiChannel);
|
display.print(midiChannel);
|
||||||
}
|
}
|
||||||
|
// Special case for tempo display
|
||||||
|
if (currentState == UI_MENU_SETUP && i == 2) {
|
||||||
|
display.print(F(": "));
|
||||||
|
display.print(tempo);
|
||||||
|
}
|
||||||
|
|
||||||
// Special case for queued theme
|
// Special case for queued theme
|
||||||
if (currentState == UI_MENU_RANDOMIZE && i >= 3 && queuedTheme == (i - 2)) {
|
if (currentState == UI_MENU_RANDOMIZE && i >= 3 && queuedTheme == (i - 2)) {
|
||||||
@ -623,6 +656,17 @@ void drawUI() {
|
|||||||
display.setCursor(0, 50);
|
display.setCursor(0, 50);
|
||||||
display.println(F(" (Press to confirm)"));
|
display.println(F(" (Press to confirm)"));
|
||||||
break;
|
break;
|
||||||
|
case UI_SETUP_TEMPO_EDIT:
|
||||||
|
display.println(F("SET TEMPO"));
|
||||||
|
display.drawLine(0, 8, 128, 8, SSD1306_WHITE);
|
||||||
|
display.setCursor(20, 25);
|
||||||
|
display.setTextSize(2);
|
||||||
|
display.print(F("BPM: "));
|
||||||
|
display.print(tempo);
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.setCursor(0, 50);
|
||||||
|
display.println(F(" (Press to confirm)"));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
display.display();
|
display.display();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user