170 if (downbeatDetector) {
216 size numBins =
fft.raw().size();
218 accent = {0.0f, 0.0f, 0.0f, 0.0f};
223 float bassEnergy = 0.0f;
224 float midEnergy = 0.0f;
225 float highEnergy = 0.0f;
228 size bassEnd =
fl::min(
static_cast<size
>(4), numBins);
229 size midStart = bassEnd;
230 size midEnd =
fl::min(
static_cast<size
>(11), numBins);
231 size highStart = midEnd;
232 size highEnd = numBins;
235 for (size i = 0; i < bassEnd; i++) {
236 bassEnergy +=
fft.raw()[i];
238 for (size i = midStart; i < midEnd; i++) {
239 midEnergy +=
fft.raw()[i];
241 for (size i = highStart; i < highEnd; i++) {
242 highEnergy +=
fft.raw()[i];
246 bassEnergy /=
static_cast<float>(bassEnd);
247 if (midEnd > midStart) {
248 midEnergy /=
static_cast<float>(midEnd - midStart);
250 if (highEnd > highStart) {
251 highEnergy /=
static_cast<float>(highEnd - highStart);
256 const float epsilon = 1e-6f;
257 const float maxRatio = 10.0f;
270 float logMaxRatio =
fl::log10f(1.0f + maxRatio);
276 accent.
bass = bassEnergy;
277 accent.
mid = midEnergy;
278 accent.
high = highEnergy;
287 accent.
total = (bassAccent * 0.3f) + (midAccent * 0.5f) + (highAccent * 0.2f);
290 return {bassEnergy, midEnergy, highEnergy, accent.
total};
314 if (!atBackbeatPosition) {
320 float accentConfidence = 0.0f;
324 if (separation > 1e-6f) {
326 accentConfidence =
fl::clamp(accentConfidence, 0.0f, 1.0f);
334 float positionConfidence = 1.0f;
342 (positionConfidence * 0.3f) +
343 (patternConfidence * 0.3f);
386 for (size i = 0; i < profileSize; i++) {
399 bool profileLearned =
false;
402 profileLearned =
true;
407 if (!profileLearned) {
414 float dotProduct = 0.0f;
415 float profileMag = 0.0f;
416 float currentMag = 0.0f;
418 for (size i = 0; i < compareSize; i++) {
421 currentMag +=
fft.raw()[i] *
fft.raw()[i];
425 const float epsilon = 1e-6f;
426 float denominator =
fl::sqrt(profileMag * currentMag);
428 if (denominator < epsilon) {
432 float correlation = dotProduct / denominator;
435 return fl::clamp(correlation, 0.0f, 1.0f);
void setDownbeatDetector(shared_ptr< Downbeat > downbeatDetector)
Share an external Downbeat instance.
shared_ptr< Beat > mBeatDetector
void fireCallbacks() override
void update(shared_ptr< Context > context) override
MultibandAccent calculateMultibandAccent(const fft::Bins &fft)
float detectBackbeatAccent(const MultibandAccent &accent)
void updateAdaptiveThresholds()
deque< float > mBackbeatAccents
MultibandAccent mPreviousAccent
void setBeatDetector(shared_ptr< Beat > beatDetector)
Share an external Beat instance.
static constexpr size MAX_ACCENT_HISTORY
function_list< void(u8 beatNumber, float confidence, float strength)> onBackbeat
Fires on detected backbeat (beats 2, 4) with beat number, confidence, and strength.
bool detectBackbeat(float accentStrength, const fft::Bins &fft)
void updateBeatDetector(shared_ptr< Context > context)
bool isBackbeatPosition() const
float calculatePatternConfidence(const fft::Bins &fft)
vector< float > mBackbeatSpectralProfile
~Backbeat() FL_NOEXCEPT override
shared_ptr< Downbeat > mDownbeatDetector
float mConfidenceThreshold
shared_ptr< const fft::Bins > mRetainedFFT
bool mOwnsDownbeatDetector
void updateBackbeatProfile(const fft::Bins &fft)
Backbeat() FL_NOEXCEPT
Construct with standalone Beat.
static constexpr size SPECTRAL_PROFILE_SIZE
Backbeat(shared_ptr< Beat > beatDetector)
Construct with shared Beat.
void updateBeatPosition()
deque< float > mNonBackbeatAccents
Multi-band accent information for backbeat detection.
FL_DISABLE_WARNING_PUSH U constexpr common_type_t< T, U > min(T a, U b) FL_NOEXCEPT
constexpr enable_if< is_fixed_point< T >::value, T >::type sqrt(T x) FL_NOEXCEPT
float log10f(float value) FL_NOEXCEPT
shared_ptr< T > make_shared(Args &&... args) 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.