feat(voice): migrate to push2talk and serialize websocket text sends
This commit is contained in:
227
components/domain/src/voice_interaction_mic_key.c
Normal file
227
components/domain/src/voice_interaction_mic_key.c
Normal file
@@ -0,0 +1,227 @@
|
||||
#include "voice_interaction_internal.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "freertos/queue.h"
|
||||
|
||||
#define VOICE_MIC_KEY_TASK_STACK 6144
|
||||
#define VOICE_MIC_KEY_QUEUE_LEN 8
|
||||
#define VOICE_MIC_KEY_SESSION_READY_WAIT_MS 2000
|
||||
#define VOICE_MIC_KEY_SESSION_READY_POLL_MS 30
|
||||
|
||||
typedef enum {
|
||||
VOICE_MIC_KEY_CMD_PRESS_DOWN = 0,
|
||||
VOICE_MIC_KEY_CMD_PRESS_UP,
|
||||
} voice_mic_key_cmd_t;
|
||||
|
||||
static const char *TAG = "voice_mic_key";
|
||||
|
||||
static QueueHandle_t s_mic_key_queue;
|
||||
static TaskHandle_t s_mic_key_task;
|
||||
static bool s_mic_key_task_with_caps;
|
||||
static bool s_mic_key_ready;
|
||||
|
||||
static bool voice_mic_key_round_busy(const voice_interaction_status_t *voice,
|
||||
const printer_runtime_status_t *printer) {
|
||||
if (voice == NULL || printer == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool marker_busy = voice->marker_image_gen_running;
|
||||
bool print_busy = printer->busy || printer->queue_depth > 0;
|
||||
return marker_busy || print_busy;
|
||||
}
|
||||
|
||||
static bool voice_mic_key_session_ready(const voice_interaction_status_t *voice) {
|
||||
return voice != NULL && voice->session_active && voice->ws_connected && voice->started;
|
||||
}
|
||||
|
||||
static bool voice_mic_key_wait_session_ready(uint32_t timeout_ms) {
|
||||
int64_t deadline_ms = voice_now_ms() + (int64_t)timeout_ms;
|
||||
while (voice_now_ms() < deadline_ms) {
|
||||
voice_interaction_status_t voice = {0};
|
||||
voice_interaction_get_status(&voice);
|
||||
if (voice_mic_key_session_ready(&voice)) {
|
||||
return true;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(VOICE_MIC_KEY_SESSION_READY_POLL_MS));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void voice_mic_key_handle_press_down(void) {
|
||||
voice_interaction_status_t voice = {0};
|
||||
voice_interaction_get_status(&voice);
|
||||
|
||||
if (voice.tap_active) {
|
||||
ESP_LOGI(TAG, "MIC_KEY press ignored: tap already active");
|
||||
return;
|
||||
}
|
||||
|
||||
printer_runtime_status_t printer = {0};
|
||||
printer_protocol_get_runtime_status(&printer);
|
||||
if (voice_mic_key_round_busy(&voice, &printer)) {
|
||||
ESP_LOGI(TAG,
|
||||
"MIC_KEY press ignored: state=%s marker_busy=%d print_busy=%d queue=%" PRIu32,
|
||||
voice_interaction_dialog_state_str(voice.dialog_state),
|
||||
voice.marker_image_gen_running,
|
||||
printer.busy,
|
||||
printer.queue_depth);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!voice_mic_key_session_ready(&voice)) {
|
||||
char start_err[128] = {0};
|
||||
esp_err_t start_rc = voice_interaction_start_with_timeout(0, start_err, sizeof(start_err));
|
||||
if (start_rc != ESP_OK) {
|
||||
ESP_LOGW(TAG,
|
||||
"MIC_KEY auto session start failed: %s (%s)",
|
||||
esp_err_to_name(start_rc),
|
||||
start_err[0] != '\0' ? start_err : "-");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!voice_mic_key_wait_session_ready(VOICE_MIC_KEY_SESSION_READY_WAIT_MS)) {
|
||||
voice_interaction_get_status(&voice);
|
||||
ESP_LOGW(TAG,
|
||||
"MIC_KEY wait session ready timeout: active=%d ws=%d started=%d",
|
||||
voice.session_active,
|
||||
voice.ws_connected,
|
||||
voice.started);
|
||||
return;
|
||||
}
|
||||
|
||||
char tap_err[128] = {0};
|
||||
esp_err_t tap_rc = voice_interaction_tap_start(tap_err, sizeof(tap_err));
|
||||
if (tap_rc != ESP_OK) {
|
||||
ESP_LOGW(TAG,
|
||||
"MIC_KEY tap start failed: %s (%s)",
|
||||
esp_err_to_name(tap_rc),
|
||||
tap_err[0] != '\0' ? tap_err : "-");
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "MIC_KEY tap started");
|
||||
}
|
||||
|
||||
static void voice_mic_key_handle_press_up(void) {
|
||||
voice_interaction_status_t voice = {0};
|
||||
voice_interaction_get_status(&voice);
|
||||
if (!voice.tap_active) {
|
||||
return;
|
||||
}
|
||||
|
||||
char tap_err[128] = {0};
|
||||
esp_err_t tap_rc = voice_interaction_tap_cancel(tap_err, sizeof(tap_err));
|
||||
if (tap_rc != ESP_OK) {
|
||||
ESP_LOGW(TAG,
|
||||
"MIC_KEY tap stop failed: %s (%s)",
|
||||
esp_err_to_name(tap_rc),
|
||||
tap_err[0] != '\0' ? tap_err : "-");
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "MIC_KEY tap stopped");
|
||||
}
|
||||
|
||||
static void voice_mic_key_task_main(void *arg) {
|
||||
(void)arg;
|
||||
|
||||
while (true) {
|
||||
voice_mic_key_cmd_t cmd = VOICE_MIC_KEY_CMD_PRESS_UP;
|
||||
if (xQueueReceive(s_mic_key_queue, &cmd, portMAX_DELAY) != pdTRUE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case VOICE_MIC_KEY_CMD_PRESS_DOWN:
|
||||
voice_mic_key_handle_press_down();
|
||||
break;
|
||||
case VOICE_MIC_KEY_CMD_PRESS_UP:
|
||||
voice_mic_key_handle_press_up();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void voice_mic_key_platform_event_cb(platform_mic_key_event_t event, void *user_data) {
|
||||
(void)user_data;
|
||||
if (s_mic_key_queue == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
voice_mic_key_cmd_t cmd = VOICE_MIC_KEY_CMD_PRESS_UP;
|
||||
switch (event) {
|
||||
case PLATFORM_MIC_KEY_EVENT_PRESS_DOWN:
|
||||
cmd = VOICE_MIC_KEY_CMD_PRESS_DOWN;
|
||||
break;
|
||||
case PLATFORM_MIC_KEY_EVENT_PRESS_UP:
|
||||
cmd = VOICE_MIC_KEY_CMD_PRESS_UP;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
BaseType_t queued = xQueueSend(s_mic_key_queue, &cmd, 0);
|
||||
if (queued == pdTRUE) {
|
||||
return;
|
||||
}
|
||||
|
||||
voice_mic_key_cmd_t dropped = VOICE_MIC_KEY_CMD_PRESS_UP;
|
||||
(void)xQueueReceive(s_mic_key_queue, &dropped, 0);
|
||||
(void)xQueueSend(s_mic_key_queue, &cmd, 0);
|
||||
}
|
||||
|
||||
esp_err_t voice_interaction_mic_key_init(void) {
|
||||
if (s_mic_key_ready) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t err = platform_mic_key_init();
|
||||
if (err == ESP_ERR_NOT_SUPPORTED) {
|
||||
ESP_LOGW(TAG, "MIC_KEY trigger disabled by board config");
|
||||
s_mic_key_ready = true;
|
||||
return ESP_OK;
|
||||
}
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
s_mic_key_queue = xQueueCreate(VOICE_MIC_KEY_QUEUE_LEN, sizeof(voice_mic_key_cmd_t));
|
||||
if (s_mic_key_queue == NULL) {
|
||||
platform_mic_key_deinit();
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
BaseType_t ok = voice_create_task_prefer_psram(voice_mic_key_task_main,
|
||||
"voice_mic_key",
|
||||
VOICE_MIC_KEY_TASK_STACK,
|
||||
4,
|
||||
&s_mic_key_task,
|
||||
&s_mic_key_task_with_caps);
|
||||
if (ok != pdPASS) {
|
||||
vQueueDelete(s_mic_key_queue);
|
||||
s_mic_key_queue = NULL;
|
||||
platform_mic_key_deinit();
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
err = platform_mic_key_set_event_callback(voice_mic_key_platform_event_cb, NULL);
|
||||
if (err != ESP_OK) {
|
||||
voice_delete_task(s_mic_key_task, s_mic_key_task_with_caps);
|
||||
s_mic_key_task = NULL;
|
||||
s_mic_key_task_with_caps = false;
|
||||
vQueueDelete(s_mic_key_queue);
|
||||
s_mic_key_queue = NULL;
|
||||
platform_mic_key_deinit();
|
||||
return err;
|
||||
}
|
||||
|
||||
s_mic_key_ready = true;
|
||||
ESP_LOGI(TAG, "MIC_KEY trigger ready: press to speak, release to process");
|
||||
return ESP_OK;
|
||||
}
|
||||
Reference in New Issue
Block a user