FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
processing-sound/src/processing/sound/BeatDetector.java at main · processing/processing-sound · GitHub
Uh oh!
There was an error while loading.
Please reload this page
.
processing
/
processing-sound
Public
Uh oh!
There was an error while loading.
Please reload this page
.
Notifications
You must be signed in to change notification settings
Fork
49
Star
163
Code
Issues
25
Pull requests
1
Actions
Security and quality
0
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Security and quality
Insights
Expand file tree
Breadcrumbs
processing-sound
/
src
/
processing
/
sound
/
BeatDetector.java
Copy path
More file actions
More file actions
Latest commit
History
History
History
261 lines (228 loc) · 7.97 KB
Breadcrumbs
processing-sound
/
src
/
processing
/
sound
/
BeatDetector.java
Copy path
File metadata and controls
261 lines (228 loc) · 7.97 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
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
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
package
processing
.
sound
;
import
com
.
jsyn
.
engine
.
SynthesisEngine
;
import
com
.
jsyn
.
ports
.
UnitInputPort
;
import
com
.
jsyn
.
ports
.
UnitOutputPort
;
import
com
.
jsyn
.
ports
.
UnitVariablePort
;
import
com
.
jsyn
.
unitgen
.
UnitGenerator
;
import
processing
.
core
.
PApplet
;
/**
* The BeatDetector analyzer looks for spikes in the energy of an audio signal
* which are often associated with rhythmic musical beats and can be used to trigger a
* response whenever the incoming audio signal pulses. Note that this
* implementation does not return a tempo or BPM (beats per minute) value — it
* can only tell you whether the current moment of audio contains a beat or not.
*
* @webref Analysis:BeatDetector
* @webBrief Looks for spikes in the energy of an audio signal
* which are often associated with rhythmic musical beats and can be used to trigger a
* response whenever the incoming audio signal pulses.
**/
public
class
BeatDetector
extends
Analyzer
{
private
final
BeatDetectorUGen
detector
;
/**
* @param parent Typically "this"
*/
public
BeatDetector
(
PApplet
parent
) {
super
(
parent
);
this
.
detector
=
new
BeatDetectorUGen
();
}
@
Override
protected
void
removeInput
() {
this
.
input
=
null
;
}
@
Override
protected
void
setInput
(
UnitOutputPort
input
) {
Engine
.
getEngine
().
add
(
this
.
detector
);
this
.
detector
.
start
();
this
.
detector
.
input
.
connect
(
input
);
}
/**
* Returns <code>true</code> if the current moment of the audio signal
* contains a beat, <code>false</code> otherwise.<br />
* A "beat" is defined as a spike in the energy of the audio signal - it may
* or may not coincide exactly with a musical beat.
*
* @webref Analysis:BeatDetector
* @webBrief Returns whether or not the current moment of audio contains a beat or not.
*/
public
boolean
isBeat
() {
return
this
.
detector
.
current
.
getValue
() ==
1
;
}
/**
* Sets the sensitivity, in milliseconds, of the beat detection algorithm.
* The sensitivity determines how long the detector will wait after detecting
* a beat to detect the next one. For example, a sensitivity of 10 will cause the
* detector to wait 10ms before returning any new beats.
*
* A higher sensitivity value means the algorithm will be less sensitive. You
* can tune this appropriately if you notice the detector returning too many
* false positive beats.
*
* @webref Analysis:BeatDetector
* @webBrief Sets the sensitivity, in milliseconds, of the beat detection algorithm.
*
* @param sensitivity Sensitivity in milliseconds. Must be a positive number.
*/
public
void
sensitivity
(
int
sensitivity
) {
this
.
detector
.
sensitivity
.
set
(
sensitivity
);
}
/**
* Sets the sensitivity of the beat detector.
* @webref Analysis:BeatDetector
* @webBrief Sets the sensitivity of the beat detector.
*
* @return The sensitivity in milliseconds.
*/
public
int
sensitivity
() {
return
(
int
)
this
.
detector
.
sensitivity
.
get
();
}
public
double
[]
getEnergyBuffer
() {
return
detector
.
getEnergyBuffer
();
}
public
int
getEnergyCursor
() {
return
detector
.
getEnergyCursor
();
}
public
boolean
[]
getBeatBuffer
() {
return
detector
.
getBeatBuffer
();
}
public
class
BeatDetectorUGen
extends
UnitGenerator
{
private
static
final
int
CHUNK_SIZE
=
1024
;
public
UnitInputPort
input
;
public
UnitVariablePort
current
;
public
UnitInputPort
sensitivity
;
public
UnitOutputPort
output
;
private
final
double
[]
audioBuffer
;
private
double
[]
energyBuffer
;
private
double
[]
deltaBuffer
;
private
boolean
[]
beatBuffer
;
private
int
audioBufferCursor
;
private
int
energyBufferCursor
;
private
long
detectTimeMillis
;
private
long
sensitivityTimer
;
public
BeatDetectorUGen
() {
this
.
addPort
(
this
.
input
=
new
UnitInputPort
(
"Input"
));
this
.
addPort
(
this
.
current
=
new
UnitVariablePort
(
"Current"
));
this
.
addPort
(
this
.
output
=
new
UnitOutputPort
(
"Output"
));
this
.
addPort
(
this
.
sensitivity
=
new
UnitInputPort
(
"Sensitivity"
));
sensitivity
.
set
(
10
);
audioBuffer
=
new
double
[
CHUNK_SIZE
];
}
@
Override
public
void
setSynthesisEngine
(
SynthesisEngine
synthesisEngine
) {
super
.
setSynthesisEngine
(
synthesisEngine
);
int
frameRate
=
synthesisEngine
.
getFrameRate
();
int
bufferSize
=
frameRate
/
CHUNK_SIZE
;
energyBuffer
=
new
double
[
bufferSize
];
deltaBuffer
=
new
double
[
bufferSize
];
beatBuffer
=
new
boolean
[
bufferSize
];
}
public
void
generate
(
int
start
,
int
limit
) {
double
[]
inputs
=
input
.
getValues
();
double
[]
outputs
=
output
.
getValues
();
for
(
int
i
=
start
;
i
<
limit
;
i
++) {
double
inputValue
=
inputs
[
i
];
audioBuffer
[
audioBufferCursor
] =
inputs
[
i
];
++
audioBufferCursor
;
// When it is full, do the FFT.
if
(
audioBufferCursor
==
audioBuffer
.
length
) {
boolean
beatDetected
=
detect
(
audioBuffer
);
current
.
set
(
beatDetected
?
1
:
0
);
audioBufferCursor
=
0
;
}
outputs
[
i
] =
inputValue
;
}
}
// This algorithm is adapted from Damien Quartz's Minim audio library
// http://code.compartmental.net/tools/minim/
private
boolean
detect
(
double
[]
samples
) {
// compute the energy level
float
level
=
0
;
for
(
int
i
=
0
;
i
<
samples
.
length
;
i
++) {
level
+= (
samples
[
i
] *
samples
[
i
]);
}
level
/=
samples
.
length
;
level
= (
float
)
Math
.
sqrt
(
level
);
float
instant
=
level
*
100
;
// compute the average local energy
float
E
=
average
(
energyBuffer
);
// compute the variance of the energies in eBuffer
float
V
=
variance
(
energyBuffer
,
E
);
// compute C using a linear digression of C with V
float
C
= (-
0.0025714f
*
V
) +
1.5142857f
;
// filter negative values
float
diff
=
Math
.
max
(
instant
-
C
*
E
,
0
);
// find the average of only the positive values in dBuffer
float
dAvg
=
specAverage
(
deltaBuffer
);
// filter negative values
float
diff2
=
Math
.
max
(
diff
-
dAvg
,
0
);
// report false if it's been less than 'sensitivity'
// milliseconds since the last true value
boolean
beatDetected
=
false
;
if
(
detectTimeMillis
-
sensitivityTimer
<
sensitivity
.
get
()) {
beatDetected
=
false
;
}
// if we've made it this far then we're allowed to set a new
// value, so set it true if it deserves to be, restart the timer
else
if
(
diff2
>
0
&&
instant
>
2
) {
beatDetected
=
true
;
sensitivityTimer
=
detectTimeMillis
;
}
// OMG it wasn't true!
else
{
beatDetected
=
false
;
}
energyBuffer
[
energyBufferCursor
] =
instant
;
deltaBuffer
[
energyBufferCursor
] =
diff
;
beatBuffer
[
energyBufferCursor
] =
beatDetected
;
energyBufferCursor
++;
if
(
energyBufferCursor
==
energyBuffer
.
length
) {
energyBufferCursor
=
0
;
}
// advance the current time by the number of milliseconds this buffer represents
detectTimeMillis
+= (
long
) (((
float
)
samples
.
length
/
getFrameRate
()) *
1000
);
return
beatDetected
;
}
private
float
average
(
double
[]
arr
) {
float
avg
=
0
;
for
(
int
i
=
0
;
i
<
arr
.
length
;
i
++) {
avg
+=
arr
[
i
];
}
avg
/=
arr
.
length
;
return
avg
;
}
private
float
specAverage
(
double
[]
arr
) {
float
avg
=
0
;
float
num
=
0
;
for
(
int
i
=
0
;
i
<
arr
.
length
;
i
++) {
if
(
arr
[
i
] >
0
) {
avg
+=
arr
[
i
];
num
++;
}
}
if
(
num
>
0
) {
avg
/=
num
;
}
return
avg
;
}
private
float
variance
(
double
[]
arr
,
float
val
) {
float
v
=
0
;
for
(
int
i
=
0
;
i
<
arr
.
length
;
i
++) {
v
+= (
float
)
Math
.
pow
(
arr
[
i
] -
val
,
2
);
}
v
/=
arr
.
length
;
return
v
;
}
public
double
[]
getEnergyBuffer
() {
return
energyBuffer
;
}
public
double
[]
getDeltaBuffer
() {
return
deltaBuffer
;
}
public
boolean
[]
getBeatBuffer
() {
return
beatBuffer
;
}
public
int
getEnergyCursor
() {
return
energyBufferCursor
;
}
}
}
Back
|
FazBrowse Home
|
New Git URL