-
Notifications
You must be signed in to change notification settings - Fork 0
/
BPM.ino
198 lines (159 loc) · 6.39 KB
/
BPM.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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
#include "BPM.hpp"
#include "MidiInput.hpp"
#include "Profiler.hpp"
bpm_status bpm = bpm_status();
void bpm_update_status( unsigned int received_ticks ) {
current_total_step = (received_ticks/TICKS_PER_STEP);
current_total_beat = current_total_step / STEPS_PER_BEAT;
current_total_bar = current_total_beat / BARS_PER_PHRASE;
current_step = current_total_step % SEQUENCE_LENGTH_STEPS;
current_beat = current_step / STEPS_PER_BEAT; //(ticks/24);//%16;
current_bar = (received_ticks/(TICKS_PER_STEP*STEPS_PER_BEAT*BEATS_PER_BAR)) % BARS_PER_PHRASE;
current_phrase = (received_ticks/(TICKS_PER_STEP*STEPS_PER_BEAT*BEATS_PER_BAR)) / BARS_PER_PHRASE;
//Serial.printf("bpm_update_status: current_phrase is %i from received_ticks %i\r\n", current_phrase, received_ticks);
is_bpm_on_beat = (0==received_ticks%PPQN);
is_bpm_on_step = (0==received_ticks%TICKS_PER_STEP);
is_bpm_on_bar = is_bpm_on_beat && current_beat == 0;
is_bpm_on_phrase = is_bpm_on_bar && (current_bar % BARS_PER_PHRASE) == 0;
if (is_bpm_on_beat) {
current_song_position = received_ticks/PPQN; // TODO: need to take into account that song position is set by the DAW sometimes....
//Serial.printf("current_beat is %i, current song position is %i\r\n", current_beat, current_song_position);
}
}
void bpm_reset_clock (int offset) {
received_ticks = 0 + offset; // set to -1 so the next tick starts us off on beat 0 step 0 tick 0
last_tick_received_at = 0;
first_tick_received_at = 0;
last_ticked = millis();
memset(last_beat_stamp,0,sizeof(last_beat_stamp)); // clear the bpm calculator history
bpm_update_status(received_ticks - offset);
harmony.reset_sequence();
Serial.printf("After reset, received_ticks is %i, current beat is %i, current step is %i\n", received_ticks, current_beat, current_step);
}
void debug_print_step_info(char *mode) {
#ifdef ENABLE_STEP_DEBUG
Serial.printf("[%s] >>BPM %3.3f >>PHRASE %i >> BAR %i >>STEP %2.2u.%1.2u ", mode, bpm_current, current_phrase, current_bar, current_beat, current_step);
Serial.printf(" (received_ticks = %.4u", received_ticks); Serial.print(") ");
Serial.print (is_bpm_on_beat ? "<<<<BEAT!" : "<< STEP ");
if (current_beat==0) {
Serial.print(" (first beat of bar)");
}
if (is_bpm_on_beat && is_bpm_on_bar && is_bpm_on_phrase) {
Serial.print(" (first beat of phrase!)");
}
Serial.println("");
#endif
}
signed long bpm_clock() {
unsigned long now = millis();
if (/*now - last_input_at > IDLE_TIMEOUT && activeNotes==0 && */now - last_tick_at > IDLE_TIMEOUT ) {
// internal mode branch
if (!bpm_internal_mode) {
// we only just switched from external to internal mode, so need to reset clock?
//playing = true;
//Serial.println("Resetting clock - bpm_clock just switched to internal bpm mode!");
bpm_reset_clock();
}
bpm_internal_mode = true;
int delta_ms = (now - last_ticked);
double ms_per_tick = (60.0f / (double)(bpm_current * (double)PPQN));
double delta_ticks = (double)delta_ms / (1000.0f*ms_per_tick);
if ((int)delta_ticks>0) {
received_ticks += delta_ticks;
bpm_update_status(received_ticks);
last_ticked = now;
if (is_bpm_on_step) {
debug_print_step_info("INT");
}
}
} else {
// external clock branched
if (bpm_internal_mode) {
// we only just switched from internal to external, so need to reset clock?
bpm_reset_clock(-1);
}
bpm_internal_mode = false;
static int last_tick;
if (last_tick!=received_ticks) {
bpm_update_status(received_ticks);
last_tick = received_ticks;
if (is_bpm_on_step) {
debug_print_step_info("EXT");
}
}
}
/*if (!playing) {
Serial.println("Not playing so BPM clock returning -1!");
return -1;
}*/
if (received_ticks==0) {
//handleStart(); // if we've reset our clock, send MIDI start
MIDIOUT.sendStart();
}
if (received_ticks%cc_value_clock_tick_ratio==0) {
midi_send_clock(received_ticks);
}
pf.l(PF::PF_BPM, millis()-now);
return received_ticks;
}
// for tracking beat times for bpm calculation
void push_beat(unsigned long duration) {
last_beat_stamp[ph] = duration;
//Serial.printf("pushed duration %i to slot %i\n", duration, ph);
ph++;
if (ph>=last_beat_sample_size) ph = 0;
}
// pinched from interwebs and modified
double average_step_length (int len) // assuming array is int.
{
long sum = 0L ; // sum will be larger than an item, long for safety.
int counted = 0;
for (int i = 0 ; i < last_beat_sample_size ; i++) {
//Serial.printf("%i: got duration %u", i, array[i]);
if (last_beat_stamp[i]==0l) continue;
sum += last_beat_stamp [i] ;
counted++;
}
if (counted >= len) {
//Serial.printf("counted %i with sum %u, returning %3.3f \r\n", counted, sum, ((double) sum) / (double)counted );
return ((double) sum) / (double)counted ; // average will be fractional, so float may be appropriate.
} else {
//Serial.printf("returning last value %3.3f\r\n", last_bpm);
return last_bpm;
}
}
double bpm_calculate_current () {
/* use this block to calculate BPM from the start of receiving ticks -- this will not track well when BPM is changed!! */
/*double beats = (double)received_ticks/(double)PPQN;
double elapsed_ms = millis() - first_tick_received_at;*/
//estimated_ticks_per_ms = received_ticks / elapsed_ms;
/* this version uses average of the last 4 received beats (actually steps) to calculate BPM so it tracks to changes better */
double beats = 1.0f / (double)last_beat_sample_size; //0.25; //1;
double elapsed_ms = average_step_length(4);
double elapsed_minutes = (elapsed_ms / 60000.0f);
double bpm = (beats / elapsed_minutes);
// speed = distance/time
// bpm = beats/time
estimated_ms_per_tick = 60000.0f/(bpm*PPQN);
//Serial.printf("bpm calculated as %3.3f (from elapsed_ms %3.3f and beats %3.3f)\r\n", bpm, elapsed_ms, beats);
return bpm;
}
void bpm_receive_clock_tick () {
received_ticks++;
unsigned long now = millis();
last_tick_at = now; //millis();
last_tick_received_at = now;
if (first_tick_received_at == 0) {
first_tick_received_at = now;
}
bpm_update_status(received_ticks);
if (is_bpm_on_step) {
push_beat(now - last_beat_at);
last_beat_at = now;
}
if (is_bpm_on_beat) {
bpm_current = bpm_calculate_current();
last_bpm = bpm_current;
}
last_input_at = now;
}