#pragma once #include class BPM : public InputSource { public: BPM() : InputSource("BPM") {} void handleEvent(const InputEvent& evt) override { if (evt.intent == InputEvent::BeatDetect) { m_nextBpm = millis(); m_timings.insert(millis()); Log.notice("%d timings", m_timings.size()); if (m_timings.size() >= 5) { updateBPM(); } } } InputEvent read() override { if (m_bpm > 0) { uint16_t now = millis(); if (now >= m_nextBpm) { m_nextBpm += m_bpm; return InputEvent{InputEvent::Beat, m_bpm}; } if (now >= m_nextLearn && m_nextLearn != 0) { m_timings.clear(); m_nextLearn = 0; } } return InputEvent{}; } private: uint16_t m_bpm = 0; uint16_t m_nextBpm = 0; uint16_t m_nextLearn = 0; Ringbuf m_timings; void updateBPM() { uint16_t avgDelta = 0; for(uint8_t i = 0; i < m_timings.size() - 1; i++) { uint16_t delta = m_timings.peek(i+1) - m_timings.peek(i); Log.notice("Timing %d Delta %d", m_timings.peek(i), delta); avgDelta += delta; } m_bpm = avgDelta / 4; m_nextLearn = m_bpm * 5 + millis(); Log.notice("BPM is now %d", m_bpm); uint16_t trash; m_timings.take(trash); } };