#include "voice_interaction_internal.h" #include #include "esp_audio_enc.h" void voice_uplink_task(void *arg) { (void)arg; while (true) { bool active = false; bool connected = false; bool started = false; bool tap_active = false; bool speech_active = false; bool listening = false; if (voice_lock(VOICE_STATUS_LOCK_TIMEOUT_MS)) { active = s_voice.session_active && !s_voice.stop_requested; connected = s_voice.ws_connected; started = s_voice.started; tap_active = s_voice.tap_active; speech_active = s_voice.speech_active; listening = (s_voice.dialog_state == VOICE_DIALOG_STATE_LISTENING); voice_unlock(); } if (!active) { break; } if (!(connected && started && listening && tap_active && speech_active)) { vTaskDelay(pdMS_TO_TICKS(20)); continue; } char read_err[64] = {0}; esp_err_t rc = voice_audio_read_pcm(s_voice.pcm_tx_buf, (size_t)s_voice.opus_enc_in_size / VOICE_SAMPLE_BYTES, VOICE_AUDIO_IO_TIMEOUT_MS, read_err, sizeof(read_err)); if (rc != ESP_OK) { memset(s_voice.pcm_tx_buf, 0, (size_t)s_voice.opus_enc_in_size); } esp_audio_enc_in_frame_t in_frame = { .buffer = (uint8_t *)s_voice.pcm_tx_buf, .len = (uint32_t)s_voice.opus_enc_in_size, }; esp_audio_enc_out_frame_t out_frame = { .buffer = s_voice.opus_tx_buf, .len = (uint32_t)s_voice.opus_enc_out_size, .encoded_bytes = 0, }; esp_audio_err_t enc_err = esp_opus_enc_process(s_voice.opus_enc, &in_frame, &out_frame); if (enc_err != ESP_AUDIO_ERR_OK || out_frame.encoded_bytes == 0) { continue; } if (voice_lock(VOICE_STATUS_LOCK_TIMEOUT_MS)) { esp_websocket_client_handle_t ws = s_voice.ws; bool ws_connected = s_voice.ws_connected; voice_unlock(); bool transport_connected = (ws != NULL) && esp_websocket_client_is_connected(ws); if (ws_connected && !transport_connected && voice_lock(VOICE_STATUS_LOCK_TIMEOUT_MS)) { if (s_voice.ws_connected) { 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(); } if (ws != NULL && ws_connected && transport_connected) { int ret = esp_websocket_client_send_bin(ws, (const char *)s_voice.opus_tx_buf, (int)out_frame.encoded_bytes, pdMS_TO_TICKS(VOICE_WS_SEND_TIMEOUT_MS)); if (ret >= 0) { if (voice_lock(VOICE_STATUS_LOCK_TIMEOUT_MS)) { s_voice.upstream_packets++; s_voice.last_event_ms = voice_now_ms(); voice_unlock(); } } } } } bool delete_with_caps = false; if (voice_lock(VOICE_STATUS_LOCK_TIMEOUT_MS)) { delete_with_caps = s_voice.uplink_task_with_caps; s_voice.uplink_task = NULL; s_voice.uplink_task_with_caps = false; voice_unlock(); } voice_delete_self_task(delete_with_caps); } void voice_heartbeat_task(void *arg) { (void)arg; while (true) { bool active = false; bool connected = false; bool started = false; if (voice_lock(VOICE_STATUS_LOCK_TIMEOUT_MS)) { active = s_voice.session_active && !s_voice.stop_requested; connected = s_voice.ws_connected; started = s_voice.started; voice_unlock(); } if (!active) { break; } if (connected && started) { (void)voice_send_directive("continue-task", "HeartBeat", true); } vTaskDelay(pdMS_TO_TICKS(CONFIG_TQ_VOICE_HEARTBEAT_SEC * 1000)); } bool delete_with_caps = false; if (voice_lock(VOICE_STATUS_LOCK_TIMEOUT_MS)) { delete_with_caps = s_voice.heartbeat_task_with_caps; s_voice.heartbeat_task = NULL; s_voice.heartbeat_task_with_caps = false; voice_unlock(); } voice_delete_self_task(delete_with_caps); }