inputs: bpm: add support for configuring startup/idle BPM
This commit is contained in:
parent
2a0d72f0a1
commit
ae3abc3aa3
@ -1,26 +1,42 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <Figments.h>
|
#include <Figments.h>
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
|
||||||
class BPM : public InputSource {
|
class BPM : public InputSource, ConfigTaskMixin {
|
||||||
public:
|
public:
|
||||||
BPM() : InputSource("BPM") {}
|
BPM() : InputSource("BPM") {}
|
||||||
void handleEvent(const InputEvent& evt) override {
|
void handleEvent(const InputEvent& evt) override {
|
||||||
if (evt.intent == InputEvent::BeatDetect) {
|
if (evt.intent == InputEvent::BeatDetect) {
|
||||||
m_nextBpm = millis();
|
m_nextBpm = millis();
|
||||||
m_timings.insert(millis());
|
m_timings.insert(millis());
|
||||||
Log.notice("%d timings", m_timings.size());
|
Log.trace("bpm: %d timings", m_timings.size());
|
||||||
if (m_timings.size() >= 5) {
|
if (m_timings.size() >= 5) {
|
||||||
updateBPM();
|
updateBPM();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ConfigTaskMixin::handleEvent(evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
InputSource::loop();
|
||||||
|
ConfigTaskMixin::loop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleConfigChange(const InputEvent& evt) override {
|
||||||
|
const JsonObject& cfg = *evt.as<JsonObject>();
|
||||||
|
if (cfg.containsKey("bpm.idle")) {
|
||||||
|
double requestedBPM = cfg["bpm.idle"];
|
||||||
|
m_msPerBeat = 60000.0 / (double)requestedBPM;
|
||||||
|
Log.notice("bpm: idle BPM set to %u (requested %lf)", msToBPM(m_msPerBeat), requestedBPM);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InputEvent read() override {
|
InputEvent read() override {
|
||||||
if (m_bpm > 0) {
|
if (m_msPerBeat > 0) {
|
||||||
uint16_t now = millis();
|
uint16_t now = millis();
|
||||||
if (now >= m_nextBpm) {
|
if (now >= m_nextBpm) {
|
||||||
m_nextBpm += m_bpm;
|
m_nextBpm += m_msPerBeat;
|
||||||
return InputEvent{InputEvent::Beat, m_bpm};
|
return InputEvent{InputEvent::Beat, msToBPM(m_msPerBeat)};
|
||||||
}
|
}
|
||||||
if (now >= m_nextLearn && m_nextLearn != 0) {
|
if (now >= m_nextLearn && m_nextLearn != 0) {
|
||||||
m_timings.clear();
|
m_timings.clear();
|
||||||
@ -31,21 +47,28 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint16_t m_bpm = 0;
|
uint16_t m_msPerBeat = 60000;
|
||||||
uint16_t m_nextBpm = 0;
|
uint16_t m_nextBpm = 0;
|
||||||
uint16_t m_nextLearn = 0;
|
uint16_t m_nextLearn = 0;
|
||||||
Ringbuf<uint16_t, 7> m_timings;
|
Ringbuf<uint16_t, 7> m_timings;
|
||||||
|
|
||||||
|
constexpr uint16_t msToBPM(float msPerBeat) const {
|
||||||
|
if (msPerBeat == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 60000.0 / msPerBeat;
|
||||||
|
}
|
||||||
|
|
||||||
void updateBPM() {
|
void updateBPM() {
|
||||||
uint16_t avgDelta = 0;
|
uint16_t avgDelta = 0;
|
||||||
for(uint8_t i = 0; i < m_timings.size() - 1; i++) {
|
for(uint8_t i = 0; i < m_timings.size() - 1; i++) {
|
||||||
uint16_t delta = m_timings.peek(i+1) - m_timings.peek(i);
|
uint16_t delta = m_timings.peek(i+1) - m_timings.peek(i);
|
||||||
Log.notice("Timing %d Delta %d", m_timings.peek(i), delta);
|
Log.trace("bpm: Timing %d Delta %d", m_timings.peek(i), delta);
|
||||||
avgDelta += delta;
|
avgDelta += delta;
|
||||||
}
|
}
|
||||||
m_bpm = avgDelta / 4;
|
m_msPerBeat = avgDelta / 4;
|
||||||
m_nextLearn = m_bpm * 5 + millis();
|
m_nextLearn = m_msPerBeat * 5 + millis();
|
||||||
Log.notice("BPM is now %d", m_bpm);
|
Log.notice("bpm: BPM is now %d", msToBPM(m_msPerBeat));
|
||||||
uint16_t trash;
|
uint16_t trash;
|
||||||
m_timings.take(trash);
|
m_timings.take(trash);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user