vibe code the initial impl
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
// features -> score -> detector, or calibration while a session is active.
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_task_wdt.h"
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "audio_in.h"
|
||||
#include "calibration.h"
|
||||
#include "config_store.h"
|
||||
#include "detector.h"
|
||||
#include "fingerprint.h"
|
||||
#include "led.h"
|
||||
#include "matcher.h"
|
||||
#include "notifier.h"
|
||||
#include "processing.h"
|
||||
|
||||
static const char *TAG = "proc";
|
||||
#define PROC_TASK_CORE 1
|
||||
#define PROC_TASK_PRIO 8
|
||||
#define PROC_TASK_STACK 6144
|
||||
|
||||
static portMUX_TYPE s_lock = portMUX_INITIALIZER_UNLOCKED;
|
||||
static proc_stats_t s_stats;
|
||||
static float s_last_fp[NUM_BANDS];
|
||||
static volatile bool s_monitor;
|
||||
static features_ctx_t s_ctx;
|
||||
|
||||
void processing_get_stats(proc_stats_t *out)
|
||||
{
|
||||
portENTER_CRITICAL(&s_lock);
|
||||
*out = s_stats;
|
||||
portEXIT_CRITICAL(&s_lock);
|
||||
}
|
||||
|
||||
void processing_get_last_fp(float fp[NUM_BANDS])
|
||||
{
|
||||
portENTER_CRITICAL(&s_lock);
|
||||
memcpy(fp, s_last_fp, sizeof(s_last_fp));
|
||||
portEXIT_CRITICAL(&s_lock);
|
||||
}
|
||||
|
||||
void processing_set_monitor(bool on) { s_monitor = on; }
|
||||
bool processing_get_monitor(void) { return s_monitor; }
|
||||
|
||||
static void update_led(app_mode_t mode, det_state_t st, bool has_tpl)
|
||||
{
|
||||
static int last = -1;
|
||||
int code;
|
||||
if (!has_tpl) code = 1;
|
||||
else if (mode != MODE_NORMAL) code = 2;
|
||||
else if (st != DET_IDLE) code = 3;
|
||||
else code = 0;
|
||||
if (code == last) return;
|
||||
last = code;
|
||||
switch (code) {
|
||||
case 1: led_set(20, 0, 0); break; // red: needs template
|
||||
case 2: led_set(20, 12, 0); break; // amber: calibration session
|
||||
case 3: led_set(20, 0, 20); break; // magenta: alert / cooldown
|
||||
default: led_set(0, 0, 6); break; // dim blue: listening
|
||||
}
|
||||
}
|
||||
|
||||
static void processing_task(void *arg)
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_task_wdt_add(NULL));
|
||||
detector_t det;
|
||||
detector_init(&det);
|
||||
float fp[NUM_BANDS];
|
||||
uint32_t last_mon_ms = 0;
|
||||
|
||||
for (;;) {
|
||||
esp_task_wdt_reset();
|
||||
audio_frame_t fr;
|
||||
if (!audio_in_get_frame(&fr, pdMS_TO_TICKS(1000))) {
|
||||
ESP_LOGW(TAG, "no audio frame for 1 s");
|
||||
continue;
|
||||
}
|
||||
|
||||
int64_t t0 = esp_timer_get_time();
|
||||
float rms = 0.0f;
|
||||
bool valid = features_compute(&s_ctx, fr.pcm, fp, &rms);
|
||||
const bool has_tpl = matcher_has_template();
|
||||
const float score = (valid && has_tpl) ? matcher_score(fp) : 0.0f;
|
||||
uint32_t dt_us = (uint32_t)(esp_timer_get_time() - t0);
|
||||
audio_in_release_frame(fr.idx); // pool slot back as early as possible
|
||||
|
||||
const uint32_t now_ms = (uint32_t)(esp_timer_get_time() / 1000);
|
||||
app_config_t cfg;
|
||||
config_get(&cfg);
|
||||
const app_mode_t mode = calibration_get_mode();
|
||||
const bool match = has_tpl && detector_is_match(rms, score, cfg.min_rms, cfg.threshold);
|
||||
|
||||
bool trig = false;
|
||||
if (mode == MODE_NORMAL) {
|
||||
if (has_tpl) {
|
||||
detector_cfg_t dc = { cfg.win_n, cfg.win_m, cfg.cooldown_ms };
|
||||
trig = detector_update(&det, match, now_ms, &dc);
|
||||
} else {
|
||||
detector_reset(&det);
|
||||
}
|
||||
} else {
|
||||
calibration_on_frame(fp, valid, rms, score); // detector paused: no alerts
|
||||
detector_reset(&det);
|
||||
}
|
||||
if (trig) {
|
||||
ESP_LOGW(TAG, "BUZZER DETECTED (rms %.0f, score %.3f)", rms, score);
|
||||
notifier_send(NOTIF_ALERT, "Buzzer detected");
|
||||
}
|
||||
|
||||
portENTER_CRITICAL(&s_lock);
|
||||
s_stats.frames++;
|
||||
s_stats.proc_avg_us = s_stats.frames == 1 ? dt_us : (s_stats.proc_avg_us * 15u + dt_us) / 16u;
|
||||
if (dt_us > s_stats.proc_max_us) s_stats.proc_max_us = dt_us;
|
||||
s_stats.last_rms = rms;
|
||||
s_stats.last_score = score;
|
||||
s_stats.last_match = match;
|
||||
s_stats.state = det.state;
|
||||
s_stats.triggers = det.triggers;
|
||||
if (trig) s_stats.last_trigger_s = now_ms / 1000 ? now_ms / 1000 : 1;
|
||||
memcpy(s_last_fp, fp, sizeof(s_last_fp));
|
||||
portEXIT_CRITICAL(&s_lock);
|
||||
|
||||
update_led(mode, det.state, has_tpl);
|
||||
|
||||
if (s_monitor && (uint32_t)(now_ms - last_mon_ms) >= cfg.monitor_ms) {
|
||||
last_mon_ms = now_ms;
|
||||
printf("[%8lu ms] rms=%7.1f score=%+.3f %s %s\n", (unsigned long)now_ms, rms, score,
|
||||
match ? "MATCH" : " - ",
|
||||
mode == MODE_NORMAL ? detector_state_name(det.state) : calibration_mode_name(mode));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t processing_start(void)
|
||||
{
|
||||
esp_err_t err = features_init();
|
||||
if (err != ESP_OK) return err;
|
||||
BaseType_t ok = xTaskCreatePinnedToCore(processing_task, "proc", PROC_TASK_STACK, NULL,
|
||||
PROC_TASK_PRIO, NULL, PROC_TASK_CORE);
|
||||
return ok == pdPASS ? ESP_OK : ESP_ERR_NO_MEM;
|
||||
}
|
||||
Reference in New Issue
Block a user