#include #include "detector.h" #include "feature_params.h" void detector_init(detector_t *d) { memset(d, 0, sizeof(*d)); d->state = DET_IDLE; } void detector_reset(detector_t *d) { d->state = DET_IDLE; d->hist = 0; d->frame_index = 0; } bool detector_is_match(float rms, float score, float min_rms, float threshold) { return rms >= min_rms && score >= threshold; } static void update_idle(detector_t *d, bool match, uint32_t now_ms, uint32_t n, uint32_t m) { uint32_t slot = d->frame_index % 32; d->match_time_ms[slot] = now_ms; d->hist = (d->hist << 1) | (match ? 1u : 0u); uint32_t mask = (n == 32) ? 0xFFFFFFFFu : ((1u << n) - 1u); if ((uint32_t)__builtin_popcount(d->hist & mask) >= m) { uint32_t matches_seen = 0; for (uint32_t offset = 0; offset < n; offset++) { if ((d->hist & (1u << offset)) && ++matches_seen == m) { uint32_t first_slot = (d->frame_index - offset) % 32; d->buzz_start_ms = d->match_time_ms[first_slot]; break; } } d->last_match_ms = now_ms; d->state = DET_TRIGGERED; d->triggers++; } d->frame_index++; } bool detector_update(detector_t *d, bool match, uint32_t now_ms, const detector_cfg_t *cfg, uint32_t *buzz_duration_ms) { uint32_t n = cfg->win_n < 1 ? 1 : (cfg->win_n > 32 ? 32 : cfg->win_n); uint32_t m = cfg->win_m < 1 ? 1 : (cfg->win_m > n ? n : cfg->win_m); switch (d->state) { case DET_IDLE: update_idle(d, match, now_ms, n, m); return false; case DET_TRIGGERED: case DET_COOLDOWN: if (match && (uint32_t)(now_ms - d->last_match_ms) < cfg->cooldown_ms) { d->last_match_ms = now_ms; d->state = DET_COOLDOWN; } else { d->state = DET_COOLDOWN; if ((uint32_t)(now_ms - d->last_match_ms) < cfg->cooldown_ms) break; d->last_completed_start_ms = d->buzz_start_ms; if (buzz_duration_ms) { *buzz_duration_ms = (uint32_t)(d->last_match_ms - d->buzz_start_ms) + (1000u * HOP_LEN / FS_HZ); } d->state = DET_IDLE; d->hist = 0; if (match) update_idle(d, true, now_ms, n, m); return true; } break; } return false; } const char *detector_state_name(det_state_t s) { switch (s) { case DET_IDLE: return "IDLE"; case DET_TRIGGERED: return "TRIGGERED"; case DET_COOLDOWN: return "COOLDOWN"; } return "?"; }