| 26 | Beat::~Beat() FL_NOEXCEPT = default; |
| 27 | |
| 28 | void Beat::update(shared_ptr<Context> context) { |
| 29 | // Use 30 Hz min frequency so bass bins actually cover sub-bass (20-60 Hz). |
| 30 | // Default fmin (90 Hz) covers most bass but misses the deepest sub-bass. |
| 31 | // CQ_NAIVE provides bass/treble discrimination via CQ kernels |
| 32 | // (which include built-in Hamming windowing), while being faster |
| 33 | // than CQ_OCTAVE. The 16-bin / 30-14080Hz range has N_window=1, |
| 34 | // which is borderline for CQ_NAIVE but acceptable for beat detection. |
| 35 | mRetainedFFT = context->getFFT(16, 30.0f, |
| 36 | fft::Args::DefaultMaxFrequency(), |
| 37 | fft::Mode::CQ_NAIVE); |
| 38 | const fft::Bins& fft = *mRetainedFFT; |
| 39 | u32 timestamp = context->getTimestamp(); |
| 40 | |
| 41 | // Calculate spectral flux |
| 42 | mSpectralFlux = calculateSpectralFlux(fft); |
| 43 | |
| 44 | // Detect beat BEFORE updating adaptive threshold. |
| 45 | // If we update the threshold first, the current frame's (potentially |
| 46 | // high) spectral flux inflates the running average, raising the |
| 47 | // threshold at the exact moment we need it lowest (onset detection). |
| 48 | mBeatDetected = detectBeat(timestamp); |
| 49 | |
| 50 | // Update adaptive threshold AFTER beat detection |
| 51 | updateAdaptiveThreshold(); |
| 52 | |
| 53 | if (mBeatDetected) { |
| 54 | updateTempo(timestamp); |
| 55 | mLastBeatTime = timestamp; |
| 56 | } |
| 57 | |
| 58 | // Update phase regardless of beat detection |
| 59 | updatePhase(timestamp); |
| 60 | |
| 61 | // Update previous magnitudes for next frame |
| 62 | for (size i = 0; i < fft.raw().size() && i < mPreviousMagnitudes.size(); i++) { |
| 63 | mPreviousMagnitudes[i] = fft.raw()[i]; |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | void Beat::reset() { |
| 68 | mBeatDetected = false; |
no test coverage detected