Midi clock and tempo
This commit is contained in:
parent
98fc15cef2
commit
5f7f241e82
@ -52,16 +52,17 @@ enum UIState {
|
||||
UI_MENU_MAIN,
|
||||
UI_MENU_RANDOMIZE,
|
||||
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 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 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*);
|
||||
|
||||
int menuSelection = 0;
|
||||
@ -77,8 +78,9 @@ const uint32_t EEPROM_MAGIC = 0x42424244;
|
||||
volatile bool isEditing = false;
|
||||
volatile int scrollOffset = 0;
|
||||
volatile bool isPlaying = false;
|
||||
unsigned long lastStepTime = 0;
|
||||
volatile int tempo = 120; // BPM
|
||||
volatile unsigned long lastClockTime = 0;
|
||||
volatile int clockCount = 0;
|
||||
|
||||
|
||||
// Encoder State
|
||||
@ -238,6 +240,12 @@ void sendMidi(uint8_t status, uint8_t note, uint8_t velocity) {
|
||||
Serial1.write(velocity);
|
||||
}
|
||||
|
||||
void sendMidiRealtime(uint8_t status) {
|
||||
mutex_enter_blocking(&midiMutex);
|
||||
Serial1.write(status);
|
||||
mutex_exit(&midiMutex);
|
||||
}
|
||||
|
||||
void generateRandomScale() {
|
||||
numScaleNotes = random(3, 13); // 3 to 12 notes
|
||||
for (int i = 0; i < 12; i++) {
|
||||
@ -325,6 +333,11 @@ void handleInput() {
|
||||
if (midiChannel < 1) midiChannel = 16;
|
||||
if (midiChannel > 16) midiChannel = 1;
|
||||
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:
|
||||
if (menuSelection == 0) { currentState = UI_MENU_MAIN; menuSelection = 2; }
|
||||
if (menuSelection == 1) currentState = UI_SETUP_CHANNEL_EDIT;
|
||||
if (menuSelection == 2) {
|
||||
if (menuSelection == 2) currentState = UI_SETUP_TEMPO_EDIT;
|
||||
if (menuSelection == 3) {
|
||||
saveSequence();
|
||||
currentState = UI_MENU_MAIN;
|
||||
}
|
||||
if (menuSelection == 3) {
|
||||
if (menuSelection == 4) {
|
||||
if (loadSequence()) showMessage("LOADED!");
|
||||
currentState = UI_MENU_MAIN;
|
||||
}
|
||||
@ -391,6 +405,9 @@ void handleInput() {
|
||||
case UI_SETUP_CHANNEL_EDIT:
|
||||
currentState = UI_MENU_SETUP;
|
||||
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"));
|
||||
if (isPlaying) {
|
||||
playbackStep = navigationSelection > 0 ? navigationSelection - 1 : 0;
|
||||
lastStepTime = millis(); // Reset timer to start immediately
|
||||
clockCount = 0;
|
||||
lastClockTime = micros();
|
||||
sendMidiRealtime(0xFA); // MIDI Start
|
||||
} else {
|
||||
// Send All Notes Off on stop (CC 123)
|
||||
needsPanic = true;
|
||||
sendMidiRealtime(0xFC); // MIDI Stop
|
||||
queuedTheme = -1;
|
||||
}
|
||||
}
|
||||
@ -420,9 +440,17 @@ void handleInput() {
|
||||
void handlePlayback() {
|
||||
if (!isPlaying) return;
|
||||
|
||||
unsigned long interval = 15000 / tempo; // 16th notes (60000 / tempo / 4)
|
||||
if (millis() - lastStepTime > interval) {
|
||||
lastStepTime = millis();
|
||||
unsigned long currentMicros = micros();
|
||||
unsigned long clockInterval = 2500000 / tempo; // 60s * 1000000us / (tempo * 24ppqn)
|
||||
|
||||
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);
|
||||
|
||||
@ -492,6 +520,11 @@ void drawMenu(const char* title, const char* items[], int count, int selection)
|
||||
display.print(F(": "));
|
||||
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
|
||||
if (currentState == UI_MENU_RANDOMIZE && i >= 3 && queuedTheme == (i - 2)) {
|
||||
@ -623,6 +656,17 @@ void drawUI() {
|
||||
display.setCursor(0, 50);
|
||||
display.println(F(" (Press to confirm)"));
|
||||
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();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user