-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBeatDetector.cpp
149 lines (128 loc) · 3.87 KB
/
BeatDetector.cpp
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
/*
* BeatDetector.cpp
*
* Created on: May 30, 2016
* Author: Adam
*/
#include "BeatDetector.h"
// Liberally uses code from http://dpeckett.com/beat-detection-on-the-arduino
// Filters generated using http://www.schwietering.com/jayduino/filtuino/index.php
// 20 - 200hz Single Pole Bandpass IIR Filter
float bassFilter(float sample) {
static float v[3] = {0, 0, 0};
v[0] = v[1];
v[1] = v[2];
v[2] = (4.114818926867040982e-1 * sample)
+ (-0.22352648289714907581 * v[0])
+ (1.11656067445431017582 * v[1]);
return
(v[2] - v[0]);
}
// 10hz Single Pole Lowpass IIR Filter
float envelopeFilter(float sample) {
static float v[2] = {0, 0};
v[0] = v[1];
v[1] = (3.046874709125380054e-2 * sample)
+ (0.93906250581749239892 * v[0]);
return
(v[0] + v[1]);
}
// 1.7 - 3.0hz Single Pole Bandpass IIR Filter
float beatFilter(float sample) {
static float v[3] = {0, 0, 0};
v[0] = v[1];
v[1] = v[2];
v[2] = (4.106189391542950512e-3 * sample)
+ (-0.99186503763064659545 * v[0])
+ (1.99166451889676765497 * v[1]);
return
(v[2] - v[0]);
}
BeatDetector::BeatDetector() {
pinMode(8, OUTPUT);
}
void BeatDetector::cast(void* param) {
((BeatDetector*)param)->taskFunc();
}
extern boolean beatDetected;
// Minimum threshold. Higher values reject more noise, at the expense of ignoring valid beats.
const float MIN_THRESH = 0.4f;
// Maximum threshold. Lower values prevent noise from spiking the threshold. Higher values allow
// more dynamic range.
const float MAX_THRESH = 100.f;
// The amount that the threshold decays per cycle. The sampling code (and thus the decay) runs
// at 1000hz.
const float THRESH_DECAY = .001f;
// What ratio of the peak incoming level to set the threshold to.
const float THRESH_RATIO = 0.5f;
TickType_t xLastWakeTime;
void BeatDetector::taskFunc() {
xLastWakeTime = xTaskGetTickCount();
float sample, value, envelope, beat;
float thresh = MIN_THRESH;
const long DEBOUNCE_MS = 100; // at 180bpm there'll be 330ms between beats
unsigned long prevHighAt = 0;
bool prevState = LOW;
// There's a brief spike in the filtered signal. Run through the sampling for a little while
// to initialize the filter state.
for (int i = 0; i < 300; i++) {
sample = (float)analogRead(INPUT_PIN)-855.f;
value = bassFilter(sample);
envelope = envelopeFilter(value);
beatFilter(envelope);
vTaskDelayUntil( &xLastWakeTime, 1 );
}
while(1) {
for (byte i=0;; i++) {
// Correct for DC offset so that with no audio signal, our input is about 0.
sample = (float)analogRead(INPUT_PIN)-855.f;
// Filter only bass component
value = bassFilter(sample);
// Take signal amplitude and filter
if (value < 0) {
value = -value;
}
envelope = envelopeFilter(value);
// Find repeating bass sounds 100 - 180bpm
beat = beatFilter(envelope);
if (beat > thresh) {
// If (THRESH_RATIO * beat) > thresh, set thresh := (THRESH_RATIO * beat)
float maybeNewThresh = beat * THRESH_RATIO;
if (maybeNewThresh > MAX_THRESH) {
maybeNewThresh = MAX_THRESH;
}
if (maybeNewThresh > thresh) {
#ifdef DEBUG
if (i % 50 == 0) {
Serial.println(maybeNewThresh);
}
#endif
thresh = maybeNewThresh;
}
//if (prevState == LOW && (millis() > (prevHighAt + DEBOUNCE_MS))) {
if (prevState == LOW) {
beatDetected = true;
prevState = HIGH;
prevHighAt = millis();
}
} else {
if (millis() > prevHighAt + DEBOUNCE_MS) {
prevState = LOW;
}
}
if (thresh > MIN_THRESH) {
thresh -= THRESH_DECAY;
} else {
thresh = MIN_THRESH;
}
// Maintain fixed sample frequency
// Note that since this is trying to delay only 1 tick, it may not maintain a very
// fixed sample frequency. The filters rely on a fixed frequency, so it may make them
// unreliable.
vTaskDelayUntil( &xLastWakeTime, 1 );
}
}
}
void BeatDetector::resetLastWakeTime() {
xLastWakeTime = xTaskGetTickCount();
}