-
Notifications
You must be signed in to change notification settings - Fork 3
/
loop.h
137 lines (105 loc) · 4.07 KB
/
loop.h
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
/*
* Copyright Ben XO https://github.com/ben-xo All rights reserved.
*/
#ifndef _LOOP_H
#define _LOOP_H
#include <Arduino.h>
#include "config.h"
#include "debug.h"
#include "sampler.h"
#include "tempo.h"
#include "framestate.h"
#include "buttons.h"
#include "hardreset.h"
#include "fps.h"
#include "demo.h"
// the loop is inlined, but filter_beat needs continuity.
extern bool filter_beat;
extern uint8_t my_current_sample;
extern uint16_t my_sample_sum;
extern Framestate F;
__attribute__((always_inline)) static void inline one_frame_sample_handler() {
DEBUG_FRAME_RATE_HIGH();
DEBUG_AUDIO_PROCESSING_RATE_HIGH();
// read these as they're volatile
my_current_sample = sampler.current_sample;
uint8_t my_new_sample_count = (my_current_sample - last_processed_sample_bd) & ~SAMP_BUFF_LEN;
// now let's do some beat calculations
bool was_beat = filter_beat;
bool is_beat_1 = false; // start calculation assuming no beat in this frame
uint8_t my_sample_base = my_current_sample - my_new_sample_count;
uint8_t offset = 0;
uint8_t sample_idx;
do {
sample_idx = (my_sample_base + offset) & ~SAMP_BUFF_LEN;
uint8_t val = sampler.samples[sample_idx];
// Serial.println(val);
filter_beat = PeckettIIRFixedPoint(val, filter_beat);
set_beat_at(sample_idx, filter_beat);
offset++;
// If there was a beat edge detected at any point, set is_beat_1.
// This gives a 1 frame resolution on beats, which is 8ms resolution at 125fps - good enough for us.
// If we only checked the end of the frame, we might miss a beat that was very short.
is_beat_1 |= filter_beat;
} while(offset < my_new_sample_count);
last_processed_sample_bd = sample_idx;
F.is_beat_1 = is_beat_1;
if(!was_beat && F.is_beat_1) {
record_rising_edge();
}
F.is_beat_2 = recalc_tempo(F.is_beat_2);
#ifdef BEAT_WITH_INTERRUPTS
// this won't be much use unless you also rip out the IIR code below…
byte is_beats = beats_from_interrupt;
F.is_beat_1 = is_beats & (1 << BEAT_PIN_1);
F.is_beat_2 = is_beats & (1 << BEAT_PIN_2);
#endif
F.min_vu = 0;
F.max_vu = 255;
// read these again, as the sampler probably sampled some stuff during the beat processing
cli();
my_current_sample = sampler.current_sample;
my_sample_sum = sample_sum;
sei();
my_new_sample_count = (my_current_sample - last_processed_sample_vu) & ~SAMP_BUFF_LEN;
last_processed_sample_vu = my_current_sample;
// Currently, the lookbehind for the VU is always the number of samples queued up since the last VU (i.e. a whole Frame's worth)
// With 5kHz sample rate and 125fps, this is usually 40 samples. But because the interrupts are staggered so they don't all fire at once,
// occassionally it's 39 or 41.
#ifndef VU_LOOKBEHIND
F.vu_width = calculate_vu(my_current_sample, &F.min_vu, &F.max_vu, my_new_sample_count);
#else
F.vu_width = calculate_vu(my_current_sample, &F.min_vu, &F.max_vu, VU_LOOKBEHIND);
#endif
#ifdef AUTOGAIN && (AUTOGAIN >= 1)
uint8_t recent_max_vu = calculate_auto_gain_bonus(F.vu_width);
F.vu_width = F.vu_width + scale8(F.vu_width, 255 - recent_max_vu);
#endif
if (F.pushed || F.vu_width > ATTRACT_MODE_THRESHOLD) {
// loudness: cancel attract mode, and so does a button press.
if(F.is_attract_mode) {
F.is_new_mode = true;
}
F.is_silent = false;
F.is_attract_mode = false;
} else {
// quiet: short or long?
if(!F.is_silent) {
// first loop of silence. Record time.
silent_since = start_time; // note start time of silence
F.is_silent = true;
} else {
// 2nd+ loop of silence. Long enough for attract mode?
if (!F.is_attract_mode && ((start_time - silent_since)/1024 > ATTRACT_MODE_TIMEOUT)) {
F.is_attract_mode = true;
}
}
}
DEBUG_AUDIO_PROCESSING_RATE_LOW();
}
__attribute__((always_inline)) static void inline frame_epilogue() {
DEBUG_FRAME_RATE_LOW();
reach_target_fps();
F.frame_counter++;
}
#endif /* _LOOP_H */