46 u32 timestamp = context->getTimestamp();
58 float bassEnergy = 0.0f;
59 for (size i = 0; i < fl::min(static_cast<size>(4),
fft.raw().size()); i++) {
60 bassEnergy +=
fft.raw()[i];
159 if (beatsPerMeasure >= 2 && beatsPerMeasure <= 16) {
196 float energyRatio = 1.0f;
202 float totalEnergy = 0.0f;
203 for (size i = 0; i <
fft.raw().size(); i++) {
204 totalEnergy +=
fft.raw()[i];
206 totalEnergy /=
static_cast<float>(
fft.raw().size());
209 float bassRatio = 1.0f;
210 if (totalEnergy > 1e-6f) {
211 bassRatio = bassEnergy / totalEnergy;
215 float accent = (energyRatio * 0.4f) + (bassRatio * 0.3f) + (totalEnergy * 0.3f);
230 float meanAccent = 1.0f;
236 meanAccent = sum /
static_cast<float>(
mBeatAccents.size());
241 float accentConfidence = meanAccent > 0.0f
254 float expectedMeasureDuration = beatInterval *
static_cast<float>(
mBeatsPerMeasure);
257 float timingError =
fl::abs(
static_cast<float>(timeSinceDownbeat) - expectedMeasureDuration);
258 float maxTimingError = beatInterval * 0.4f;
259 bool nearMeasureBoundary = (timingError < maxTimingError);
262 float meanAccent = 1.0f;
268 meanAccent = sum /
static_cast<float>(
mBeatAccents.size());
277 float timingConfidence = 1.0f - (timingError / (beatInterval * 2.0f));
278 timingConfidence =
fl::clamp(timingConfidence, 0.0f, 1.0f);
280 float accentConfidence = meanAccent > 0.0f
286 float accentWeight = atBeatCounterBoundary ? 0.7f : 0.5f;
287 float timingWeight = atBeatCounterBoundary ? 0.3f : 0.5f;
288 mConfidence = (timingConfidence * timingWeight) + (accentConfidence * accentWeight);
299 if (atBeatCounterBoundary) {
302 }
else if (nearMeasureBoundary && strongAccent) {
327 float bestScore = 0.0f;
330 for (size m = 0; m < candidateMeters.
size(); m++) {
331 u8 meter = candidateMeters[m];
335 for (size i = 0; i < numAccents; i++) {
336 if (i % meter == 0) {
345 if (score > bestScore) {
351 detectedMeter = bestMeter;
388 float measureDuration = beatInterval *
static_cast<float>(
mBeatsPerMeasure);
390 if (measureDuration > 0.0f) {
391 mMeasurePhase =
static_cast<float>(timeSinceDownbeat) / measureDuration;
412 if (meter >= 2 && meter < 16) {
419 u8 mostCommonMeter = 4;
421 for (
u8 meter = 2; meter < 16; meter++) {
422 if (counts[meter] > maxCount) {
423 maxCount = counts[meter];
424 mostCommonMeter = meter;
428 return mostCommonMeter;
fl::size size() const FL_NOEXCEPT
A fixed-size array implementation similar to std::array.
void setBeatDetector(shared_ptr< Beat > beatDetector)
Share an external Beat instance.
deque< u8 > mMeterCandidates
float calculateBeatAccent(const fft::Bins &fft, float bassEnergy)
bool isDownbeat() const
Returns true if downbeat was detected this frame.
~Downbeat() FL_NOEXCEPT override
deque< float > mBeatAccents
function_list< void(float phase)> onMeasurePhase
Fires with measure phase each frame (0-1 range)
Downbeat(shared_ptr< Beat > beatDetector)
Construct with shared Beat (recommended)
Downbeat() FL_NOEXCEPT
Construct with standalone Beat.
void setTimeSignature(u8 beatsPerMeasure)
Manually set time signature (disables auto-detection)
u8 findMostCommonMeter() const
function_list< void()> onDownbeat
Fires on detected downbeat (first beat of measure)
void fireCallbacks() override
float mConfidenceThreshold
shared_ptr< Beat > mBeatDetector
void update(shared_ptr< Context > context) override
void updateMeasurePhase(u32 timestamp)
void updateBeatDetector(shared_ptr< Context > context)
function_list< void(u8 beatsPerMeasure)> onMeterChange
Fires when time signature changes.
static constexpr size METER_HISTORY_SIZE
static constexpr size MAX_BEAT_HISTORY
function_list< void(u8 beatNumber)> onMeasureBeat
Fires on each beat with beat number (1-based, downbeat = 1)
shared_ptr< const fft::Bins > mRetainedFFT
bool detectDownbeat(u32 timestamp, float accent)
constexpr common_type_t< T, U > max(T a, U b) FL_NOEXCEPT
shared_ptr< T > make_shared(Args &&... args) FL_NOEXCEPT
constexpr enable_if< is_fixed_point< T >::value, T >::type abs(T x) FL_NOEXCEPT
constexpr enable_if< is_fixed_point< T >::value, T >::type clamp(T x, T lo, T hi) FL_NOEXCEPT
Base definition for an LED controller.