54class PlaybackHandler {
60 void playButtonPressed(int32_t buttonPressLatency);
61 void recordButtonPressed();
62 void setupPlaybackUsingInternalClock(int32_t buttonPressLatencyForTempolessRecord = 0,
bool allowCountIn =
true,
63 bool restartingPlayback =
false,
bool restartingPlaybackAtBeginning =
false);
64 void setupPlaybackUsingExternalClock(
bool switchingFromInternalClock =
false,
bool fromContinueCommand =
false);
65 void setupPlayback(int32_t newPlaybackState, int32_t playFromPos,
bool doOneLastAudioRoutineCall =
false,
66 bool shouldShiftAccordingToClipInstance =
true,
67 int32_t buttonPressLatencyForTempolessRecord = 0);
69 void inputTick(
bool fromTriggerClock =
false, uint32_t time = 0);
70 void startMessageReceived();
71 void continueMessageReceived();
72 void stopMessageReceived();
73 void clockMessageReceived(uint32_t time);
74 void tempoEncoderAction(int8_t offset,
bool encoderButtonPressed,
bool shiftButtonPressed);
75 bool isCurrentlyRecording();
76 void positionPointerReceived(uint8_t data1, uint8_t data2);
77 void doSongSwap(
bool preservePlayPosition =
false);
78 void forceResetPlayPos(
Song* song,
bool restartingPlaybackAtBeginning =
false);
80 void setMidiInClockEnabled(
bool newValue);
81 int32_t getActualArrangementRecordPos();
82 int32_t getArrangementRecordPosAtLastActionedSwungTick();
84 void scheduleSwungTickFromExternalClock();
85 int32_t getNumSwungTicksInSinceLastTimerTick(uint32_t* timeRemainder =
nullptr);
86 int32_t getNumSwungTicksInSinceLastActionedSwungTick(uint32_t* timeRemainder =
nullptr);
87 int64_t getActualSwungTickCount(uint32_t* timeRemainder =
nullptr);
88 int64_t getCurrentInternalTickCount(uint32_t* remainder =
nullptr);
89 void scheduleSwungTick();
90 int32_t getInternalTickTime(int64_t internalTickCount);
91 void scheduleTriggerClockOutTick();
92 void scheduleMIDIClockOutTick();
93 void scheduleNextTimerTick(uint32_t doubleSwingInterval);
96 bool midiClockOutTickScheduled;
97 uint32_t timeNextMIDIClockOutTick;
98 int64_t lastMIDIClockOutTickDone;
101 uint8_t playbackState;
102 bool usingAnalogClockInput;
103 RecordingMode recording;
104 bool ignoringMidiClockInput;
106 int32_t posToNextContinuePlaybackFrom;
107 uint32_t timeLastMIDIStartOrContinueMessageSent;
110 int64_t lastTimerTickActioned;
111 int64_t nextTimerTickScheduled;
113 uint64_t timeNextTimerTickBig;
114 uint64_t timeLastTimerTickBig;
117 uint32_t timeLastInputTicks[kNumInputTicksForMovingAverage];
118 uint32_t timePerInputTickMovingAverage;
119 uint8_t numInputTickTimesCounted;
121 bool tempoMagnitudeMatchingActiveNow;
124 timeVeryFirstInputTick;
125 int64_t lastInputTickReceived;
126 unsigned long targetedTimePerInputTick;
129 bool swungTickScheduled;
130 uint32_t scheduledSwungTickTime;
137 int64_t lastSwungTickActioned;
140 bool triggerClockOutTickScheduled;
141 uint32_t timeNextTriggerClockOutTick;
142 int64_t lastTriggerClockOutTickDone;
144 uint32_t analogOutTicksPPQN;
145 uint32_t analogInTicksPPQN;
146 uint32_t timeLastAnalogClockInputRisingEdge;
147 bool analogClockInputAutoStart;
149 bool songSwapShouldPreserveTempo;
153 bool midiOutClockEnabled;
154 bool midiInClockEnabled;
155 bool tempoMagnitudeMatchingEnabled;
158 int32_t swungTicksTilNextEvent;
160 int32_t ticksLeftInCountIn;
161 int32_t currentVisualCountForCountIn;
163 int32_t metronomeOffset;
166 void tapTempoAutoSwitchOff();
167 void reassessInputTickScaling();
168 void resyncInternalTicksToInputTicks(
Song* song);
169 bool shouldRecordNotesNow();
170 void stopAnyRecording();
171 uint32_t getTimePerInternalTick();
172 uint64_t getTimePerInternalTickBig();
173 float getTimePerInternalTickFloat();
174 uint32_t getTimePerInternalTickInverse(
bool getStickyValue =
false);
175 void tapTempoButtonPress(
bool useNormalTapTempoBehaviour);
176 void doTriggerClockOutTick();
177 void doMIDIClockOutTick();
178 void resyncAnalogOutTicksToInternalTicks();
179 void resyncMIDIClockOutTicksToInternalTicks();
180 void analogClockRisingEdge(uint32_t time);
181 void toggleMetronomeStatus();
182 void commandDisplayTempo();
183 void setMidiOutClockMode(
bool newValue);
184 void pitchBendReceived(
MIDICable& cable, uint8_t channel, uint8_t data1, uint8_t data2,
bool* doingMidiThru);
185 void midiCCReceived(
MIDICable& cable, uint8_t channel, uint8_t ccNumber, uint8_t value,
bool* doingMidiThru);
186 void programChangeReceived(
MIDICable& cable, int32_t channel, int32_t program);
187 void aftertouchReceived(
MIDICable& cable, int32_t channel, int32_t value, int32_t noteCode,
188 bool* doingMidiThru);
189 void loopCommand(OverDubType overdubNature);
190 void grabTempoFromClip(
Clip* clip);
191 int32_t getTimeLeftInCountIn();
193 void noteMessageReceived(
MIDICable& cable,
bool on, int32_t channel, int32_t note, int32_t velocity,
194 bool* doingMidiThru);
195 bool subModeAllowsRecording();
197 float calculateBPM(
float timePerInternalTick);
198 void switchToArrangement();
199 void switchToSession();
200 void finishTempolessRecording(
bool startPlaybackAgain, int32_t buttonLatencyForTempolessRecord,
201 bool shouldExitRecordMode =
true);
203 int32_t arrangementPosToStartAtOnSwitch;
205 bool stopOutputRecordingAtLoopEnd;
207 void actionTimerTick();
208 void actionTimerTickPart2();
209 void actionSwungTick();
210 void scheduleSwungTickFromInternalClock();
211 bool currentlySendingMIDIOutputClocks();
213 inline bool isExternalClockActive() {
return (playbackState & PLAYBACK_CLOCK_EXTERNAL_ACTIVE); }
214 inline bool isInternalClockActive() {
return (playbackState & PLAYBACK_CLOCK_INTERNAL_ACTIVE); }
215 inline bool isEitherClockActive() {
return (playbackState & PLAYBACK_CLOCK_EITHER_ACTIVE); }
219 void commandEditSwingAmount(int8_t offset);
221 void commandEditSwingInterval(int8_t offset);
222 void commandNudgeClock(int8_t offset);
223 void commandEditClockOutScale(int8_t offset);
224 void commandEditTempoCoarse(int8_t offset);
225 void commandEditTempoFine(int8_t offset);
226 void commandDisplayTempo(int8_t offset);
227 void commandClearTempoAutomation();
229 void getTempoStringForOLED(
float tempoBPM,
StringBuf& buffer);
231 void tryLoopCommand(GlobalMIDICommand command);
233 float calculateBPMForDisplay();
236 uint32_t timerTicksToOutputTicks(uint32_t timerTicks);
239 uint32_t timePerInternalTickMovingAverage;
240 uint32_t veryCurrentTimePerInternalTickInverse;
241 uint32_t stickyCurrentTimePerInternalTickInverse;
242 uint32_t lowpassedTimePerInternalTick;
243 uint32_t slowpassedTimePerInternalTick;
244 uint32_t stickyTimePerInternalTick;
246 uint16_t tapTempoNumPresses;
247 uint32_t tapTempoFirstPressTime;
249 int32_t numOutputClocksWaitingToBeSent;
250 int32_t numInputTicksToSkip;
251 uint32_t skipAnalogClocks;
252 uint32_t skipMidiClocks;
254 void resetTimePerInternalTickMovingAverage();
255 void getCurrentTempoParams(int32_t* magnitude, int8_t* whichValue);
256 void displayTempoFromParams(int32_t magnitude, int8_t whichValue);
257 void displayTempoBPM(
float tempoBPM);
258 void getAnalogOutTicksToInternalTicksRatio(uint32_t* internalTicksPer, uint32_t* analogOutTicksPer);
259 void getMIDIClockOutTicksToInternalTicksRatio(uint32_t* internalTicksPer, uint32_t* midiClockOutTicksPer);
260 void getInternalTicksToInputTicksRatio(uint32_t* inputTicksPer, uint32_t* internalTicksPer);
261 void sendOutPositionViaMIDI(int32_t pos,
bool outputClocksWereSwitchedOff =
false);
263 bool startIgnoringMidiClockInputIfNecessary();
264 uint32_t setTempoFromAudioClipLength(uint64_t loopLengthSamples,
Action* action);
265 bool offerNoteToLearnedThings(
MIDICable& cable,
bool on, int32_t channel, int32_t note);
266 bool tryGlobalMIDICommands(
MIDICable& cable, int32_t channel, int32_t note);
267 bool tryGlobalMIDICommandsOff(
MIDICable& cable, int32_t channel, int32_t note);
268 void decideOnCurrentPlaybackMode();
269 float getCurrentInternalTickFloatFollowingExternalClock();
270 void scheduleTriggerClockOutTickParamsKnown(uint32_t analogOutTicksPer, uint64_t fractionLastTimerTick,
271 uint64_t fractionNextAnalogOutTick);
272 void scheduleMIDIClockOutTickParamsKnown(uint32_t midiClockOutTicksPer, uint64_t fractionLastTimerTick,
273 uint64_t fractionNextMIDIClockOutTick);