feat(voice): migrate to push2talk and serialize websocket text sends
This commit is contained in:
@@ -88,7 +88,7 @@ static char *voice_build_start_message(void) {
|
||||
cJSON *parameters = cJSON_AddObjectToObject(payload, "parameters");
|
||||
cJSON *upstream = cJSON_AddObjectToObject(parameters, "upstream");
|
||||
cJSON_AddStringToObject(upstream, "type", "AudioOnly");
|
||||
cJSON_AddStringToObject(upstream, "mode", "tap2talk");
|
||||
cJSON_AddStringToObject(upstream, "mode", "push2talk");
|
||||
cJSON_AddStringToObject(upstream, "audio_format", "raw-opus");
|
||||
cJSON_AddNumberToObject(upstream, "sample_rate", CONFIG_TQ_VOICE_SAMPLE_RATE);
|
||||
|
||||
@@ -113,7 +113,7 @@ static char *voice_build_start_message(void) {
|
||||
return text;
|
||||
}
|
||||
|
||||
static esp_err_t voice_send_text(const char *text, int len) {
|
||||
static esp_err_t voice_send_text_immediate(const char *text, int len) {
|
||||
if (text == NULL || len <= 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
@@ -132,6 +132,7 @@ static esp_err_t voice_send_text(const char *text, int len) {
|
||||
s_voice.ws_connected = false;
|
||||
s_voice.started = false;
|
||||
s_voice.tap_active = false;
|
||||
s_voice.speech_active = false;
|
||||
s_voice.dialog_state = VOICE_DIALOG_STATE_IDLE;
|
||||
s_voice.last_event_ms = voice_now_ms();
|
||||
ESP_LOGW(TAG, "[stage] ws_state_reconciled: transport disconnected");
|
||||
@@ -156,6 +157,43 @@ static esp_err_t voice_send_text(const char *text, int len) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t voice_send_text(const char *text, int len) {
|
||||
if (text == NULL || len <= 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
QueueHandle_t queue = NULL;
|
||||
TaskHandle_t ws_text_task = NULL;
|
||||
bool session_active = false;
|
||||
if (voice_lock(VOICE_STATUS_LOCK_TIMEOUT_MS)) {
|
||||
queue = s_voice.ws_text_queue;
|
||||
ws_text_task = s_voice.ws_text_task;
|
||||
session_active = s_voice.session_active;
|
||||
voice_unlock();
|
||||
}
|
||||
|
||||
if (queue == NULL || ws_text_task == NULL || !session_active) {
|
||||
return voice_send_text_immediate(text, len);
|
||||
}
|
||||
|
||||
char *copy = (char *)voice_malloc_prefer_psram((size_t)len + 1);
|
||||
if (copy == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
memcpy(copy, text, (size_t)len);
|
||||
copy[len] = '\0';
|
||||
|
||||
voice_ws_text_msg_t msg = {
|
||||
.text = copy,
|
||||
.len = len,
|
||||
};
|
||||
if (xQueueSend(queue, &msg, pdMS_TO_TICKS(VOICE_WS_SEND_TIMEOUT_MS)) != pdTRUE) {
|
||||
free(copy);
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t voice_send_directive(const char *action,
|
||||
const char *directive,
|
||||
bool with_dialog_id) {
|
||||
@@ -181,6 +219,68 @@ esp_err_t voice_send_directive(const char *action,
|
||||
return err;
|
||||
}
|
||||
|
||||
void voice_ws_text_task(void *arg) {
|
||||
(void)arg;
|
||||
|
||||
while (true) {
|
||||
bool shutdown = false;
|
||||
QueueHandle_t queue = NULL;
|
||||
if (voice_lock(VOICE_STATUS_LOCK_TIMEOUT_MS)) {
|
||||
shutdown = s_voice.ws_text_shutdown;
|
||||
queue = s_voice.ws_text_queue;
|
||||
voice_unlock();
|
||||
}
|
||||
|
||||
if (shutdown || queue == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
voice_ws_text_msg_t msg = {0};
|
||||
if (xQueueReceive(queue, &msg, pdMS_TO_TICKS(VOICE_WS_TEXT_QUEUE_RX_TIMEOUT_MS)) != pdTRUE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (voice_lock(VOICE_STATUS_LOCK_TIMEOUT_MS)) {
|
||||
s_voice.ws_text_busy = true;
|
||||
voice_unlock();
|
||||
}
|
||||
|
||||
esp_err_t err = voice_send_text_immediate(msg.text, msg.len);
|
||||
if (err != ESP_OK && voice_lock(VOICE_STATUS_LOCK_TIMEOUT_MS)) {
|
||||
voice_set_last_error_locked("ws text send failed");
|
||||
voice_unlock();
|
||||
}
|
||||
|
||||
if (voice_lock(VOICE_STATUS_LOCK_TIMEOUT_MS)) {
|
||||
s_voice.ws_text_busy = false;
|
||||
voice_unlock();
|
||||
}
|
||||
free(msg.text);
|
||||
}
|
||||
|
||||
QueueHandle_t queue = NULL;
|
||||
if (voice_lock(VOICE_STATUS_LOCK_TIMEOUT_MS)) {
|
||||
queue = s_voice.ws_text_queue;
|
||||
s_voice.ws_text_busy = false;
|
||||
voice_unlock();
|
||||
}
|
||||
if (queue != NULL) {
|
||||
voice_ws_text_msg_t msg = {0};
|
||||
while (xQueueReceive(queue, &msg, 0) == pdTRUE) {
|
||||
free(msg.text);
|
||||
}
|
||||
}
|
||||
|
||||
bool delete_with_caps = false;
|
||||
if (voice_lock(VOICE_STATUS_LOCK_TIMEOUT_MS)) {
|
||||
delete_with_caps = s_voice.ws_text_task_with_caps;
|
||||
s_voice.ws_text_task = NULL;
|
||||
s_voice.ws_text_task_with_caps = false;
|
||||
voice_unlock();
|
||||
}
|
||||
voice_delete_self_task(delete_with_caps);
|
||||
}
|
||||
|
||||
static void voice_handle_text_chunk(esp_websocket_event_data_t *data) {
|
||||
if (data->payload_offset == 0) {
|
||||
free(s_voice.text_agg);
|
||||
@@ -310,6 +410,7 @@ void voice_websocket_event_handler(void *handler_args,
|
||||
s_voice.ws_connected = false;
|
||||
s_voice.started = false;
|
||||
s_voice.tap_active = false;
|
||||
s_voice.speech_active = false;
|
||||
s_voice.dialog_state = VOICE_DIALOG_STATE_IDLE;
|
||||
s_voice.last_event_ms = voice_now_ms();
|
||||
voice_unlock();
|
||||
|
||||
Reference in New Issue
Block a user