Files
buzz_detector/main/processing.c
T

153 lines
5.1 KiB
C

// 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 buzz_ended = false;
uint32_t buzz_duration_ms = 0;
if (mode == MODE_NORMAL) {
if (has_tpl) {
detector_cfg_t dc = { cfg.win_n, cfg.win_m, cfg.cooldown_ms };
buzz_ended = detector_update(&det, match, now_ms, &dc, &buzz_duration_ms);
} else {
detector_reset(&det);
}
} else {
calibration_on_frame(fp, valid, rms, score); // detector paused: no alerts
detector_reset(&det);
}
if (buzz_ended) {
char message[96];
snprintf(message, sizeof(message), "Buzzer detected: %lu ms",
(unsigned long)buzz_duration_ms);
ESP_LOGW(TAG, "BUZZER EVENT ended, detected duration %lu ms",
(unsigned long)buzz_duration_ms);
notifier_send(buzz_duration_ms);
}
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 (buzz_ended) {
s_stats.last_trigger_s = det.last_completed_start_ms / 1000;
if (s_stats.last_trigger_s == 0) s_stats.last_trigger_s = 1;
s_stats.last_buzz_ms = buzz_duration_ms;
}
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;
}