forked from rogerlinndesign/linnstrument-firmware
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ls_clock.ino
106 lines (81 loc) · 3.78 KB
/
ls_clock.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/***************************** ls_clock: LinnStrument Musical Clock *******************************
This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License.
To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/
or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
***************************************************************************************************
These functions keep track of a common musical system clock in 24PPQ.
When incoming MIDI clock is running it will be used, otherwise an internal clock based on the
active tempo will be calculated.
**************************************************************************************************/
const unsigned long INTERNAL_CLOCK_UNIT_BASE = 2500000; // 1000000 ( microsecond) * 60 ( minutes - bpm) / 24 ( frames per beat)
unsigned long prevClockTimerCount; // the last time the microsecond timer was updated for the musical clock
unsigned long lastInternalClockMoment; // the last time the internal clock stepped
unsigned char lastInternalClockCount; // the count of the internal clock steps, from 0 to 23
signed char previousMidiClockCount; // the previous MIDI clock count, used to detect if a change really occurred
signed char previousInternalClockCount; // the previous internal clock count, used to detect if a change really occurred
void initializeClock() {
prevClockTimerCount = micros();
clock24PPQ = 0;
previousMidiClockCount = 0;
lastInternalClockCount = 0;
resetClockAdvancement(0);
}
void resetClockAdvancement(unsigned long now) {
lastInternalClockMoment = now;
lastInternalClockCount = 0;
previousMidiClockCount = -1;
previousInternalClockCount = -1;
}
inline boolean checkUpdateClock(unsigned long now) {
short clockCount;
if (isSyncedToMidiClock()) {
clockCount = getMidiClockCount();
if (previousMidiClockCount == clockCount) {
return false;
}
previousMidiClockCount = clockCount;
}
else {
if (calcTimeDelta(now, prevClockTimerCount) <= 500) {
return false;
}
prevClockTimerCount = now;
// calculate the time since the last clock tick
unsigned long internalClockDelta = calcTimeDelta(now, lastInternalClockMoment);
unsigned long clockUnit = INTERNAL_CLOCK_UNIT_BASE / FXD4_TO_INT(fxd4CurrentTempo);
// check if the time since the last clock step now has exceeded the delay for the next step, but only if within 10ms of the intended step duration
if (internalClockDelta >= clockUnit && (internalClockDelta % clockUnit) < 10000) {
lastInternalClockCount = (lastInternalClockCount + 1) % 24;
lastInternalClockMoment += ((now - lastInternalClockMoment) / clockUnit) * clockUnit;
// flash the tempo led in the global display when it is on
updateGlobalSettingsFlashTempo(now);
if (previousInternalClockCount == lastInternalClockCount) {
return false;
}
clockCount = lastInternalClockCount;
previousInternalClockCount = clockCount;
}
else {
return false;
}
}
clock24PPQ = clockCount;
if (!isSyncedToMidiClock() &&
(sequencerIsRunning() || isStandaloneMidiClockRunning())) {
midiSendTimingClock();
}
return true;
}
void tapTempoPress() {
unsigned long now = micros();
resetClockAdvancement(now);
unsigned long tapDelta = calcTimeDelta(now, lastTapTempo);
if (tapDelta < 6000000) { // maximum 6 seconds between taps
fxd4CurrentTempo -= FXD4_DIV(fxd4CurrentTempo, FXD4_FROM_INT(4));
fxd4CurrentTempo += FXD4_DIV(FXD4_FROM_INT(60000000 / tapDelta), FXD4_FROM_INT(4));
}
lastTapTempo = now;
if (displayMode == displayGlobal) {
setDisplayMode(displayGlobalWithTempo);
}
}