142 lines
4.5 KiB
C++
142 lines
4.5 KiB
C++
#include "MidiDriver.h"
|
|
#include "TrackerTypes.h"
|
|
#include "UIThread.h"
|
|
#include "config.h"
|
|
#include "SharedState.h"
|
|
|
|
bool wasPlaying = false;
|
|
|
|
static void handlePlayback() {
|
|
bool nowPlaying = isPlaying;
|
|
int tracksToPlay = (playMode == MODE_POLY) ? NUM_TRACKS : 1;
|
|
if (!wasPlaying && !nowPlaying) {
|
|
midi.sendRealtime(0xFA); // MIDI Start
|
|
} else if (wasPlaying && !nowPlaying) {
|
|
midi.sendRealtime(0xFC); // MIDI Stop
|
|
for (int i=0; i<tracksToPlay; i++) midi.panic(midiChannels[i]);
|
|
}
|
|
wasPlaying = nowPlaying;
|
|
|
|
if (!nowPlaying) return;
|
|
|
|
unsigned long currentMicros = micros();
|
|
unsigned long clockInterval = 2500000 / tempo; // 60s * 1000000us / (tempo * 24ppqn)
|
|
|
|
if (currentMicros - lastClockTime >= clockInterval) {
|
|
lastClockTime += clockInterval;
|
|
|
|
midi.sendRealtime(0xF8); // MIDI Clock
|
|
|
|
clockCount++;
|
|
if (clockCount < 6) return; // 24 ppqn / 4 = 6 pulses per 16th note
|
|
clockCount = 0;
|
|
|
|
midi.lock();
|
|
Step local_sequence[NUM_TRACKS][NUM_STEPS];
|
|
memcpy(local_sequence, sequence, sizeof(local_sequence));
|
|
Step local_nextSequence[NUM_TRACKS][NUM_STEPS];
|
|
memcpy(local_nextSequence, nextSequence, sizeof(local_nextSequence));
|
|
midi.unlock();
|
|
|
|
for(int t=0; t<tracksToPlay; t++) {
|
|
int trackChannel = playMode == MODE_POLY ? midiChannels[t] : midiChannels[0];
|
|
int nextStep = playbackStep + 1;
|
|
if (nextStep >= NUM_STEPS) nextStep = 0;
|
|
|
|
// Determine if we are tying to the next note
|
|
bool isTied = local_sequence[t][playbackStep].tie && (local_sequence[t][nextStep].note != -1);
|
|
int prevNote = local_sequence[t][playbackStep].note;
|
|
|
|
// Note Off for previous step (if NOT tied)
|
|
if (!isTied && prevNote != -1) {
|
|
midi.sendNoteOff(prevNote, trackChannel);
|
|
}
|
|
}
|
|
|
|
playbackStep++;
|
|
if (playbackStep >= NUM_STEPS) {
|
|
playbackStep = 0;
|
|
|
|
// Theme change
|
|
if (sequenceChangeScheduled && queuedTheme != -1) {
|
|
currentThemeIndex = queuedTheme;
|
|
queuedTheme = -1;
|
|
// nextSequence is already generated
|
|
}
|
|
|
|
// Mutation
|
|
if (mutationEnabled) {
|
|
if (!sequenceChangeScheduled) {
|
|
memcpy(local_nextSequence, local_sequence, sizeof(sequence));
|
|
}
|
|
mutateSequence(local_nextSequence);
|
|
midi.lock();
|
|
memcpy(nextSequence, local_nextSequence, sizeof(sequence));
|
|
sequenceChangeScheduled = true;
|
|
midi.unlock();
|
|
}
|
|
|
|
for (int i=0; i<tracksToPlay; i++) midi.panic(midiChannels[i]);
|
|
|
|
if (sequenceChangeScheduled) {
|
|
memcpy(local_sequence, local_nextSequence, sizeof(local_sequence));
|
|
midi.lock();
|
|
memcpy(sequence, local_sequence, sizeof(local_sequence));
|
|
sequenceChangeScheduled = false;
|
|
midi.unlock();
|
|
}
|
|
|
|
// Song Mode? Advance repeats
|
|
if (songModeEnabled) {
|
|
// we just used one repeat
|
|
if (songRepeatsRemaining <= 1) {
|
|
// let's start another round
|
|
songRepeatsRemaining = nextSongRepeats;
|
|
} else {
|
|
// next repeat
|
|
songRepeatsRemaining--;
|
|
}
|
|
// Trigger next song segment generation if we are on the last repeat
|
|
if (songRepeatsRemaining <= 1 && !sequenceChangeScheduled && !songModeNeedsNext) {
|
|
songModeNeedsNext = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Note On for new step
|
|
for(int t=0; t<tracksToPlay; t++) {
|
|
int trackChannel = playMode == MODE_POLY ? midiChannels[t] : midiChannels[0];
|
|
if (!trackMute[t] && local_sequence[t][playbackStep].note != -1) {
|
|
uint8_t velocity = local_sequence[t][playbackStep].accent ? 127 : 100;
|
|
midi.sendNoteOn(local_sequence[t][playbackStep].note, velocity, trackChannel);
|
|
}
|
|
|
|
int prevStep = (playbackStep == 0) ? NUM_STEPS - 1 : playbackStep - 1;
|
|
bool wasTied = local_sequence[t][prevStep].tie && (local_sequence[t][playbackStep].note != -1);
|
|
int prevNote = local_sequence[t][prevStep].note;
|
|
// Note Off for previous step (if tied - delayed Note Off)
|
|
if (wasTied && prevNote != -1) {
|
|
midi.sendNoteOff(prevNote, trackChannel);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void loopPlayback() {
|
|
unsigned long now = millis();
|
|
lastLoop1Time = now;
|
|
if (watchdogActive && (now - lastLoop0Time > 1000)) {
|
|
Serial.println("Core 0 Freeze detected");
|
|
rp2040.reboot();
|
|
}
|
|
|
|
if (needsPanic) {
|
|
if (playMode == MODE_POLY) {
|
|
for (int i=0; i<NUM_TRACKS; i++) midi.panic(midiChannels[i]);
|
|
} else {
|
|
midi.panic(midiChannels[0]);
|
|
}
|
|
needsPanic = false;
|
|
}
|
|
handlePlayback();
|
|
} |