Simplify false hit detection

For now, only allow one input at a time
This commit is contained in:
Frederik Walk 2023-12-03 15:34:57 +01:00
parent b17b98068a
commit 092eeb4f07
9 changed files with 45 additions and 86 deletions

View File

@ -47,7 +47,8 @@ Few things which you probably want to change more regularly can be changed using
- Controller emulation mode - Controller emulation mode
- LED brightness - LED brightness
- Trigger thresholds and scale level - Trigger thresholds
- Hold Time
- Enter BOOTSEL mode for firmware flashing - Enter BOOTSEL mode for firmware flashing
Those settings are persisted to flash memory if you choose 'Save' when exiting the Menu and will survive power cycles. Those settings are persisted to flash memory if you choose 'Save' when exiting the Menu and will survive power cycles.
@ -60,14 +61,6 @@ The debounce delay also implicitly serves as the hold time of the input after a
If you notice dropped inputs even if the controller signals a hit on the LED/Display, try to increase this value. If you notice dropped inputs even if the controller signals a hit on the LED/Display, try to increase this value.
### Trigger Scale Level
To avoid false inputs for very hard hits, trigger levels of the non-hit pads are scaled dynamically. The trigger scale level defines how much trigger levels are increased.
Increase the value if you get false inputs on hard hits, decrease if simultaneous left and right hits are not registered properly.
Mind that the scaling is non-linear and changes in the higher end of the scale level range have a much larger impact.
## Hardware ## Hardware
### IO Board ### IO Board

View File

@ -44,9 +44,8 @@ const Peripherals::Drum::Config drum_config = {
80, // Don Right 80, // Don Right
50, // Ka Right 50, // Ka Right
}, },
230, // Trigger threshold scale level
50, // ADC sample count 10, // ADC sample count
25, // Debounce delay in milliseconds 25, // Debounce delay in milliseconds
true, // Use external ADC true, // Use external ADC

View File

@ -32,7 +32,6 @@ class Drum {
AdcInputs adc_inputs; AdcInputs adc_inputs;
Thresholds trigger_thresholds; Thresholds trigger_thresholds;
uint8_t trigger_threshold_scale_level;
uint8_t sample_count; uint8_t sample_count;
uint16_t debounce_delay_ms; uint16_t debounce_delay_ms;
@ -51,6 +50,7 @@ class Drum {
private: private:
enum class Id { enum class Id {
NONE,
DON_LEFT, DON_LEFT,
KA_LEFT, KA_LEFT,
DON_RIGHT, DON_RIGHT,
@ -64,11 +64,11 @@ class Drum {
bool active; bool active;
public: public:
Pad(uint8_t channel); Pad(const uint8_t channel);
uint8_t getChannel() const { return channel; }; uint8_t getChannel() const { return channel; };
bool getState() const { return active; }; bool getState() const { return active; };
void setState(bool state, uint16_t debounce_delay); void setState(const bool state, const uint16_t debounce_delay);
}; };
class AdcInterface { class AdcInterface {
@ -105,7 +105,6 @@ class Drum {
void setDebounceDelay(const uint16_t delay); void setDebounceDelay(const uint16_t delay);
void setThresholds(const Config::Thresholds &thresholds); void setThresholds(const Config::Thresholds &thresholds);
void setThresholdScaleLevel(const uint8_t threshold_scale_level);
}; };
} // namespace Doncon::Peripherals } // namespace Doncon::Peripherals

View File

@ -23,7 +23,6 @@ class Menu {
TriggerThresholdDonLeft, TriggerThresholdDonLeft,
TriggerThresholdDonRight, TriggerThresholdDonRight,
TriggerThresholdKaRight, TriggerThresholdKaRight,
TriggerThresholdScaleLevel,
DebounceDelay, DebounceDelay,
LedBrightness, LedBrightness,
Reset, Reset,
@ -54,7 +53,6 @@ class Menu {
GotoPageTriggerThresholdDonLeft, GotoPageTriggerThresholdDonLeft,
GotoPageTriggerThresholdDonRight, GotoPageTriggerThresholdDonRight,
GotoPageTriggerThresholdKaRight, GotoPageTriggerThresholdKaRight,
GotoPageTriggerThresholdScaleLevel,
GotoPageDebounceDelay, GotoPageDebounceDelay,
GotoPageLedBrightness, GotoPageLedBrightness,
GotoPageReset, GotoPageReset,
@ -74,7 +72,6 @@ class Menu {
SetTriggerThresholdDonLeft, SetTriggerThresholdDonLeft,
SetTriggerThresholdDonRight, SetTriggerThresholdDonRight,
SetTriggerThresholdKaRight, SetTriggerThresholdKaRight,
SetTriggerThresholdScaleLevel,
SetDebounceDelay, SetDebounceDelay,
SetLedBrightness, SetLedBrightness,

View File

@ -20,13 +20,11 @@ class SettingsStore {
uint8_t in_use; uint8_t in_use;
usb_mode_t usb_mode; usb_mode_t usb_mode;
Peripherals::Drum::Config::Thresholds trigger_thresholds; Peripherals::Drum::Config::Thresholds trigger_thresholds;
uint8_t trigger_threshold_scale_level;
uint8_t led_brightness; uint8_t led_brightness;
uint16_t debounce_delay; uint16_t debounce_delay;
uint8_t _padding[m_store_size - sizeof(uint8_t) - sizeof(usb_mode_t) - uint8_t _padding[m_store_size - sizeof(uint8_t) - sizeof(usb_mode_t) -
sizeof(Peripherals::Drum::Config::Thresholds) - sizeof(uint8_t) - sizeof(uint8_t) - sizeof(Peripherals::Drum::Config::Thresholds) - sizeof(uint8_t) - sizeof(uint16_t)];
sizeof(uint16_t)];
}; };
static_assert(sizeof(Storecache) == m_store_size); static_assert(sizeof(Storecache) == m_store_size);
@ -53,9 +51,6 @@ class SettingsStore {
void setTriggerThresholds(const Peripherals::Drum::Config::Thresholds &thresholds); void setTriggerThresholds(const Peripherals::Drum::Config::Thresholds &thresholds);
Peripherals::Drum::Config::Thresholds getTriggerThresholds(); Peripherals::Drum::Config::Thresholds getTriggerThresholds();
void setTriggerThresholdScaleLevel(const uint8_t threshold_scale_level);
uint8_t getTriggerThresholdScaleLevel();
void setLedBrightness(const uint8_t brightness); void setLedBrightness(const uint8_t brightness);
uint8_t getLedBrightness(); uint8_t getLedBrightness();

View File

@ -92,8 +92,6 @@ void core1_task() {
led.update(); led.update();
display.update(); display.update();
// sleep_ms(1);
} }
} }
@ -131,7 +129,6 @@ int main() {
drum.setDebounceDelay(settings_store->getDebounceDelay()); drum.setDebounceDelay(settings_store->getDebounceDelay());
drum.setThresholds(settings_store->getTriggerThresholds()); drum.setThresholds(settings_store->getTriggerThresholds());
drum.setThresholdScaleLevel(settings_store->getTriggerThresholdScaleLevel());
}; };
readSettings(); readSettings();

View File

@ -49,9 +49,9 @@ Drum::ExternalAdc::ExternalAdc(const Drum::Config::Spi &spi_config) : m_mcp3204(
uint16_t Drum::ExternalAdc::read(uint8_t channel) { return m_mcp3204.read(channel); } uint16_t Drum::ExternalAdc::read(uint8_t channel) { return m_mcp3204.read(channel); }
Drum::Pad::Pad(uint8_t channel) : channel(channel), last_change(0), active(false) {} Drum::Pad::Pad(const uint8_t channel) : channel(channel), last_change(0), active(false) {}
void Drum::Pad::setState(bool state, uint16_t debounce_delay) { void Drum::Pad::setState(const bool state, const uint16_t debounce_delay) {
if (active == state) { if (active == state) {
return; return;
} }
@ -99,28 +99,37 @@ void Drum::updateInputState(Utils::InputState &input_state) {
// Oversample ADC inputs to get rid of ADC noise // Oversample ADC inputs to get rid of ADC noise
const auto raw_values = sampleInputs(); const auto raw_values = sampleInputs();
const auto max_value = std::max_element(raw_values.cbegin(), raw_values.cend(), auto get_threshold = [&](const Id target) {
[](const auto a, const auto b) { return a.second < b.second; }); switch (target) {
case Id::DON_LEFT:
auto do_set_state = [&](const auto &id, const auto &threshold) { return m_config.trigger_thresholds.don_left;
// Increase threshold for very hard hits to avoid false inputs on neighboring pads case Id::DON_RIGHT:
float mult = 1.0; return m_config.trigger_thresholds.don_right;
if (id != max_value->first && m_config.trigger_threshold_scale_level > 0) { case Id::KA_LEFT:
mult = float(max_value->second) / (((UINT8_MAX * 16) - (m_config.trigger_threshold_scale_level * 16)) + 1); return m_config.trigger_thresholds.ka_left;
mult = mult < 1.0 ? 1.0 : mult; case Id::KA_RIGHT:
} return m_config.trigger_thresholds.ka_right;
case Id::NONE:
if (raw_values.at(id) > (threshold * mult)) { return (uint16_t)0;
m_pads.at(id).setState(true, m_config.debounce_delay_ms);
} else {
m_pads.at(id).setState(false, m_config.debounce_delay_ms);
} }
return (uint16_t)0;
}; };
do_set_state(Id::DON_LEFT, m_config.trigger_thresholds.don_left); // Consider the hardest hit pad the one that actually was hit,
do_set_state(Id::KA_LEFT, m_config.trigger_thresholds.ka_left); const auto max_hit = std::max_element(raw_values.cbegin(), raw_values.cend(),
do_set_state(Id::DON_RIGHT, m_config.trigger_thresholds.don_right); [](const auto a, const auto b) { return a.second < b.second; });
do_set_state(Id::KA_RIGHT, m_config.trigger_thresholds.ka_right);
if (max_hit->second > get_threshold(max_hit->first)) {
m_pads.at(max_hit->first).setState(true, m_config.debounce_delay_ms);
} else {
m_pads.at(max_hit->first).setState(false, m_config.debounce_delay_ms);
}
for (const auto &input : raw_values) {
if (input.first != max_hit->first) {
m_pads.at(input.first).setState(false, m_config.debounce_delay_ms);
}
}
input_state.drum.don_left.raw = raw_values.at(Id::DON_LEFT); input_state.drum.don_left.raw = raw_values.at(Id::DON_LEFT);
input_state.drum.ka_left.raw = raw_values.at(Id::KA_LEFT); input_state.drum.ka_left.raw = raw_values.at(Id::KA_LEFT);
@ -137,8 +146,4 @@ void Drum::setDebounceDelay(const uint16_t delay) { m_config.debounce_delay_ms =
void Drum::setThresholds(const Config::Thresholds &thresholds) { m_config.trigger_thresholds = thresholds; } void Drum::setThresholds(const Config::Thresholds &thresholds) { m_config.trigger_thresholds = thresholds; }
void Drum::setThresholdScaleLevel(const uint8_t threshold_scale_level) {
m_config.trigger_threshold_scale_level = threshold_scale_level;
}
} // namespace Doncon::Peripherals } // namespace Doncon::Peripherals

View File

@ -28,15 +28,14 @@ const std::map<Menu::Page, const Menu::Descriptor> Menu::descriptors = {
{"Debug", Menu::Descriptor::Action::ChangeUsbModeDebug}}, // {"Debug", Menu::Descriptor::Action::ChangeUsbModeDebug}}, //
0}}, // 0}}, //
{Menu::Page::TriggerThreshold, // {Menu::Page::TriggerThreshold, //
{Menu::Descriptor::Type::Selection, // {Menu::Descriptor::Type::Selection, //
"Sensitivity", // "Sensitivity", //
{{"Ka Left", Menu::Descriptor::Action::GotoPageTriggerThresholdKaLeft}, // {{"Ka Left", Menu::Descriptor::Action::GotoPageTriggerThresholdKaLeft}, //
{"Don Left", Menu::Descriptor::Action::GotoPageTriggerThresholdDonLeft}, // {"Don Left", Menu::Descriptor::Action::GotoPageTriggerThresholdDonLeft}, //
{"Don Right", Menu::Descriptor::Action::GotoPageTriggerThresholdDonRight}, // {"Don Right", Menu::Descriptor::Action::GotoPageTriggerThresholdDonRight}, //
{"Ka Right", Menu::Descriptor::Action::GotoPageTriggerThresholdKaRight}, // {"Ka Right", Menu::Descriptor::Action::GotoPageTriggerThresholdKaRight}}, //
{"Scale Lvl", Menu::Descriptor::Action::GotoPageTriggerThresholdScaleLevel}}, // 0}}, //
0}}, //
{Menu::Page::TriggerThresholdKaLeft, // {Menu::Page::TriggerThresholdKaLeft, //
{Menu::Descriptor::Type::Value, // {Menu::Descriptor::Type::Value, //
@ -62,12 +61,6 @@ const std::map<Menu::Page, const Menu::Descriptor> Menu::descriptors = {
{{"", Menu::Descriptor::Action::SetTriggerThresholdKaRight}}, // {{"", Menu::Descriptor::Action::SetTriggerThresholdKaRight}}, //
4095}}, 4095}},
{Menu::Page::TriggerThresholdScaleLevel, //
{Menu::Descriptor::Type::Value, //
"Sensitivity Scale Lvl", //
{{"", Menu::Descriptor::Action::SetTriggerThresholdScaleLevel}}, //
UINT8_MAX}},
{Menu::Page::DebounceDelay, // {Menu::Page::DebounceDelay, //
{Menu::Descriptor::Type::Value, // {Menu::Descriptor::Type::Value, //
"Hit Hold Time (ms)", // "Hit Hold Time (ms)", //
@ -202,9 +195,6 @@ uint16_t Menu::getCurrentSelection(Menu::Page page) {
case Page::TriggerThresholdKaRight: case Page::TriggerThresholdKaRight:
return m_store->getTriggerThresholds().ka_right; return m_store->getTriggerThresholds().ka_right;
break; break;
case Page::TriggerThresholdScaleLevel:
return m_store->getTriggerThresholdScaleLevel();
break;
case Page::DebounceDelay: case Page::DebounceDelay:
return m_store->getDebounceDelay(); return m_store->getDebounceDelay();
break; break;
@ -252,9 +242,6 @@ void Menu::performSelectionAction(Menu::Descriptor::Action action) {
case Descriptor::Action::GotoPageTriggerThresholdKaRight: case Descriptor::Action::GotoPageTriggerThresholdKaRight:
gotoPage(Page::TriggerThresholdKaRight); gotoPage(Page::TriggerThresholdKaRight);
break; break;
case Descriptor::Action::GotoPageTriggerThresholdScaleLevel:
gotoPage(Page::TriggerThresholdScaleLevel);
break;
case Descriptor::Action::GotoPageLedBrightness: case Descriptor::Action::GotoPageLedBrightness:
gotoPage(Page::LedBrightness); gotoPage(Page::LedBrightness);
break; break;
@ -307,7 +294,6 @@ void Menu::performSelectionAction(Menu::Descriptor::Action action) {
case Descriptor::Action::SetTriggerThresholdDonLeft: case Descriptor::Action::SetTriggerThresholdDonLeft:
case Descriptor::Action::SetTriggerThresholdDonRight: case Descriptor::Action::SetTriggerThresholdDonRight:
case Descriptor::Action::SetTriggerThresholdKaRight: case Descriptor::Action::SetTriggerThresholdKaRight:
case Descriptor::Action::SetTriggerThresholdScaleLevel:
gotoParent(); gotoParent();
break; break;
case Descriptor::Action::SetDebounceDelay: case Descriptor::Action::SetDebounceDelay:
@ -359,9 +345,6 @@ void Menu::performValueAction(Menu::Descriptor::Action action, uint16_t value) {
thresholds.ka_right = value; thresholds.ka_right = value;
m_store->setTriggerThresholds(thresholds); m_store->setTriggerThresholds(thresholds);
} break; } break;
case Descriptor::Action::SetTriggerThresholdScaleLevel:
m_store->setTriggerThresholdScaleLevel(value);
break;
case Descriptor::Action::SetDebounceDelay: case Descriptor::Action::SetDebounceDelay:
m_store->setDebounceDelay(value); m_store->setDebounceDelay(value);
break; break;

View File

@ -14,7 +14,6 @@ SettingsStore::SettingsStore()
: m_store_cache({m_magic_byte, : m_store_cache({m_magic_byte,
Config::Default::usb_mode, Config::Default::usb_mode,
Config::Default::drum_config.trigger_thresholds, Config::Default::drum_config.trigger_thresholds,
Config::Default::drum_config.trigger_threshold_scale_level,
Config::Default::led_config.brightness, Config::Default::led_config.brightness,
Config::Default::drum_config.debounce_delay_ms, Config::Default::drum_config.debounce_delay_ms,
{}}), {}}),
@ -59,14 +58,6 @@ void SettingsStore::setTriggerThresholds(const Peripherals::Drum::Config::Thresh
} }
Peripherals::Drum::Config::Thresholds SettingsStore::getTriggerThresholds() { return m_store_cache.trigger_thresholds; } Peripherals::Drum::Config::Thresholds SettingsStore::getTriggerThresholds() { return m_store_cache.trigger_thresholds; }
void SettingsStore::setTriggerThresholdScaleLevel(const uint8_t threshold_scale_level) {
if (threshold_scale_level != m_store_cache.trigger_threshold_scale_level) {
m_store_cache.trigger_threshold_scale_level = threshold_scale_level;
m_dirty = true;
}
}
uint8_t SettingsStore::getTriggerThresholdScaleLevel() { return m_store_cache.trigger_threshold_scale_level; }
void SettingsStore::setLedBrightness(const uint8_t brightness) { void SettingsStore::setLedBrightness(const uint8_t brightness) {
if (m_store_cache.led_brightness != brightness) { if (m_store_cache.led_brightness != brightness) {
m_store_cache.led_brightness = brightness; m_store_cache.led_brightness = brightness;