feat(control_plane): play prompt before light standby
This commit is contained in:
@@ -41,6 +41,14 @@
|
|||||||
#define CONFIG_TQ_LIGHT_STANDBY_BACKLIGHT_PERCENT 10
|
#define CONFIG_TQ_LIGHT_STANDBY_BACKLIGHT_PERCENT 10
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef CONFIG_TQ_LIGHT_STANDBY_PROMPT_ENABLE
|
||||||
|
#define CONFIG_TQ_LIGHT_STANDBY_PROMPT_ENABLE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CONFIG_TQ_LIGHT_STANDBY_PROMPT_FILE
|
||||||
|
#define CONFIG_TQ_LIGHT_STANDBY_PROMPT_FILE "standby_5min.opus"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef CONFIG_TQ_LIGHT_STANDBY_DISABLE_SPEAKER_OUTPUT
|
#ifndef CONFIG_TQ_LIGHT_STANDBY_DISABLE_SPEAKER_OUTPUT
|
||||||
#define CONFIG_TQ_LIGHT_STANDBY_DISABLE_SPEAKER_OUTPUT 0
|
#define CONFIG_TQ_LIGHT_STANDBY_DISABLE_SPEAKER_OUTPUT 0
|
||||||
#endif
|
#endif
|
||||||
@@ -317,6 +325,44 @@ static esp_err_t control_plane_light_standby_apply_entry_policy(void)
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static esp_err_t control_plane_light_standby_play_entry_prompt(void)
|
||||||
|
{
|
||||||
|
#if CONFIG_TQ_LIGHT_STANDBY_PROMPT_ENABLE
|
||||||
|
TickType_t started_tick = xTaskGetTickCount();
|
||||||
|
ESP_LOGI(TAG,
|
||||||
|
"Entry prompt started file=%s",
|
||||||
|
CONFIG_TQ_LIGHT_STANDBY_PROMPT_FILE);
|
||||||
|
esp_err_t prompt_err = domain_runtime_behavior_play_prompt_file(
|
||||||
|
CONFIG_TQ_LIGHT_STANDBY_PROMPT_FILE);
|
||||||
|
uint32_t elapsed_ms = (uint32_t)pdTICKS_TO_MS(
|
||||||
|
xTaskGetTickCount() - started_tick);
|
||||||
|
if (prompt_err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG,
|
||||||
|
"Entry prompt failed elapsed_ms=%u err=%s",
|
||||||
|
(unsigned)elapsed_ms,
|
||||||
|
esp_err_to_name(prompt_err));
|
||||||
|
return prompt_err;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG,
|
||||||
|
"Entry prompt completed elapsed_ms=%u",
|
||||||
|
(unsigned)elapsed_ms);
|
||||||
|
#endif
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool control_plane_light_standby_entry_is_current(
|
||||||
|
uint64_t entry_activity_ms)
|
||||||
|
{
|
||||||
|
bool current;
|
||||||
|
portENTER_CRITICAL(&s_standby_lock);
|
||||||
|
current = s_standby.running &&
|
||||||
|
!s_standby.stop_requested &&
|
||||||
|
s_standby.state == CONTROL_PLANE_LIGHT_STANDBY_STATE_ENTERING &&
|
||||||
|
s_standby.last_activity_ms == entry_activity_ms;
|
||||||
|
portEXIT_CRITICAL(&s_standby_lock);
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
static void control_plane_light_standby_enter(uint64_t now_ms)
|
static void control_plane_light_standby_enter(uint64_t now_ms)
|
||||||
{
|
{
|
||||||
if (!control_plane_light_standby_lock_transition()) {
|
if (!control_plane_light_standby_lock_transition()) {
|
||||||
@@ -349,27 +395,41 @@ static void control_plane_light_standby_enter(uint64_t now_ms)
|
|||||||
CONTROL_PLANE_LIGHT_STANDBY_STATE_ENTERING,
|
CONTROL_PLANE_LIGHT_STANDBY_STATE_ENTERING,
|
||||||
"idle_timeout");
|
"idle_timeout");
|
||||||
|
|
||||||
esp_err_t policy_err = control_plane_light_standby_apply_entry_policy();
|
esp_err_t policy_err = control_plane_light_standby_play_entry_prompt();
|
||||||
|
bool policy_applied = false;
|
||||||
|
bool entry_current = policy_err == ESP_OK &&
|
||||||
|
control_plane_light_standby_entry_is_current(entry_activity_ms);
|
||||||
|
bool runtime_eligible = entry_current &&
|
||||||
|
control_plane_light_standby_runtime_is_eligible();
|
||||||
|
if (runtime_eligible) {
|
||||||
|
policy_applied = true;
|
||||||
|
policy_err = control_plane_light_standby_apply_entry_policy();
|
||||||
|
}
|
||||||
bool entered = false;
|
bool entered = false;
|
||||||
bool restore_required = false;
|
bool restore_required = false;
|
||||||
const char *completion_reason = "hardware_policy";
|
const char *completion_reason = "hardware_policy";
|
||||||
|
uint64_t completion_ms = control_plane_light_standby_now_ms();
|
||||||
portENTER_CRITICAL(&s_standby_lock);
|
portENTER_CRITICAL(&s_standby_lock);
|
||||||
if (s_standby.state == CONTROL_PLANE_LIGHT_STANDBY_STATE_ENTERING &&
|
if (s_standby.state == CONTROL_PLANE_LIGHT_STANDBY_STATE_ENTERING &&
|
||||||
!s_standby.stop_requested &&
|
!s_standby.stop_requested &&
|
||||||
s_standby.last_activity_ms == entry_activity_ms &&
|
s_standby.last_activity_ms == entry_activity_ms &&
|
||||||
|
policy_applied &&
|
||||||
policy_err == ESP_OK) {
|
policy_err == ESP_OK) {
|
||||||
s_standby.state = CONTROL_PLANE_LIGHT_STANDBY_STATE_STANDBY;
|
s_standby.state = CONTROL_PLANE_LIGHT_STANDBY_STATE_STANDBY;
|
||||||
s_standby.standby_since_ms = now_ms;
|
s_standby.standby_since_ms = completion_ms;
|
||||||
s_standby.transition_count++;
|
s_standby.transition_count++;
|
||||||
entered = true;
|
entered = true;
|
||||||
} else if (s_standby.state == CONTROL_PLANE_LIGHT_STANDBY_STATE_ENTERING) {
|
} else if (s_standby.state == CONTROL_PLANE_LIGHT_STANDBY_STATE_ENTERING) {
|
||||||
s_standby.state = CONTROL_PLANE_LIGHT_STANDBY_STATE_WAKING;
|
s_standby.state = CONTROL_PLANE_LIGHT_STANDBY_STATE_WAKING;
|
||||||
s_standby.transition_count++;
|
s_standby.transition_count++;
|
||||||
restore_required = true;
|
restore_required = policy_applied;
|
||||||
if (policy_err != ESP_OK) {
|
if (policy_err != ESP_OK) {
|
||||||
completion_reason = "hardware_policy_failed";
|
completion_reason = policy_applied ?
|
||||||
|
"hardware_policy_failed" : "entry_prompt_failed";
|
||||||
} else if (s_standby.stop_requested) {
|
} else if (s_standby.stop_requested) {
|
||||||
completion_reason = "stop_requested";
|
completion_reason = "stop_requested";
|
||||||
|
} else if (entry_current && !runtime_eligible) {
|
||||||
|
completion_reason = "runtime_ineligible";
|
||||||
} else {
|
} else {
|
||||||
completion_reason = "activity_during_entry";
|
completion_reason = "activity_during_entry";
|
||||||
}
|
}
|
||||||
@@ -399,7 +459,7 @@ static void control_plane_light_standby_enter(uint64_t now_ms)
|
|||||||
s_standby.state = CONTROL_PLANE_LIGHT_STANDBY_STATE_ACTIVE;
|
s_standby.state = CONTROL_PLANE_LIGHT_STANDBY_STATE_ACTIVE;
|
||||||
s_standby.standby_since_ms = 0U;
|
s_standby.standby_since_ms = 0U;
|
||||||
if (policy_err != ESP_OK) {
|
if (policy_err != ESP_OK) {
|
||||||
s_standby.last_activity_ms = now_ms;
|
s_standby.last_activity_ms = completion_ms;
|
||||||
}
|
}
|
||||||
s_standby.transition_count++;
|
s_standby.transition_count++;
|
||||||
activated = true;
|
activated = true;
|
||||||
@@ -631,8 +691,7 @@ esp_err_t control_plane_light_standby_record_activity(
|
|||||||
s_standby.last_activity = activity;
|
s_standby.last_activity = activity;
|
||||||
s_standby.last_activity_ms = now_ms;
|
s_standby.last_activity_ms = now_ms;
|
||||||
task = s_standby.task;
|
task = s_standby.task;
|
||||||
if (s_standby.state == CONTROL_PLANE_LIGHT_STANDBY_STATE_STANDBY ||
|
if (s_standby.state == CONTROL_PLANE_LIGHT_STANDBY_STATE_STANDBY) {
|
||||||
s_standby.state == CONTROL_PLANE_LIGHT_STANDBY_STATE_ENTERING) {
|
|
||||||
wake_required = true;
|
wake_required = true;
|
||||||
}
|
}
|
||||||
portEXIT_CRITICAL(&s_standby_lock);
|
portEXIT_CRITICAL(&s_standby_lock);
|
||||||
|
|||||||
@@ -51,6 +51,9 @@ esp_err_t multimodal_audio_unregister_pcm_observer(
|
|||||||
esp_err_t multimodal_audio_handle_downstream_opus(const uint8_t *data, size_t len);
|
esp_err_t multimodal_audio_handle_downstream_opus(const uint8_t *data, size_t len);
|
||||||
esp_err_t multimodal_audio_begin_downstream_response(uint32_t *out_playback_generation);
|
esp_err_t multimodal_audio_begin_downstream_response(uint32_t *out_playback_generation);
|
||||||
esp_err_t multimodal_audio_finish_downstream_response(void);
|
esp_err_t multimodal_audio_finish_downstream_response(void);
|
||||||
|
esp_err_t multimodal_audio_wait_downstream_playback_complete(
|
||||||
|
uint32_t playback_generation,
|
||||||
|
int timeout_ms);
|
||||||
esp_err_t multimodal_audio_stop_downstream_playback(void);
|
esp_err_t multimodal_audio_stop_downstream_playback(void);
|
||||||
esp_err_t multimodal_audio_register_downstream_playback_callback(
|
esp_err_t multimodal_audio_register_downstream_playback_callback(
|
||||||
multimodal_audio_downstream_playback_callback_t cb,
|
multimodal_audio_downstream_playback_callback_t cb,
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/idf_additions.h"
|
#include "freertos/idf_additions.h"
|
||||||
#include "freertos/queue.h"
|
#include "freertos/queue.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "soc/soc_caps.h"
|
#include "soc/soc_caps.h"
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
@@ -356,6 +357,16 @@ typedef struct {
|
|||||||
static multimodal_audio_ctx_t s_audio;
|
static multimodal_audio_ctx_t s_audio;
|
||||||
static multimodal_audio_downstream_playback_callback_t s_downstream_playback_cb;
|
static multimodal_audio_downstream_playback_callback_t s_downstream_playback_cb;
|
||||||
static void *s_downstream_playback_cb_ctx;
|
static void *s_downstream_playback_cb_ctx;
|
||||||
|
static SemaphoreHandle_t s_downstream_playback_completion_sem;
|
||||||
|
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
||||||
|
static StaticSemaphore_t s_downstream_playback_completion_sem_storage;
|
||||||
|
#endif
|
||||||
|
static bool s_downstream_playback_wait_registered;
|
||||||
|
static uint32_t s_downstream_playback_wait_generation;
|
||||||
|
static bool s_downstream_last_terminal_valid;
|
||||||
|
static uint32_t s_downstream_last_terminal_generation;
|
||||||
|
static multimodal_audio_downstream_playback_event_type_t
|
||||||
|
s_downstream_last_terminal_type;
|
||||||
static multimodal_audio_pcm_observer_t s_pcm_observer;
|
static multimodal_audio_pcm_observer_t s_pcm_observer;
|
||||||
static void *s_pcm_observer_ctx;
|
static void *s_pcm_observer_ctx;
|
||||||
static uint32_t s_pcm_observer_in_flight;
|
static uint32_t s_pcm_observer_in_flight;
|
||||||
|
|||||||
@@ -1212,10 +1212,29 @@ esp_err_t multimodal_audio_init(void)
|
|||||||
ESP_LOGW(TAG, "Codec path disabled by configuration");
|
ESP_LOGW(TAG, "Codec path disabled by configuration");
|
||||||
return ESP_ERR_NOT_SUPPORTED;
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
#else
|
#else
|
||||||
|
if (s_downstream_playback_completion_sem == NULL) {
|
||||||
|
#if ( configSUPPORT_STATIC_ALLOCATION == 1 )
|
||||||
|
s_downstream_playback_completion_sem = xSemaphoreCreateBinaryStatic(
|
||||||
|
&s_downstream_playback_completion_sem_storage);
|
||||||
|
#else
|
||||||
|
s_downstream_playback_completion_sem = xSemaphoreCreateBinary();
|
||||||
|
#endif
|
||||||
|
if (s_downstream_playback_completion_sem == NULL) {
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (s_audio.initialized) {
|
if (s_audio.initialized) {
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(void)xSemaphoreTake(s_downstream_playback_completion_sem, 0U);
|
||||||
|
portENTER_CRITICAL(&s_audio_runtime_lock);
|
||||||
|
s_downstream_playback_wait_registered = false;
|
||||||
|
s_downstream_playback_wait_generation = 0U;
|
||||||
|
s_downstream_last_terminal_valid = false;
|
||||||
|
s_downstream_last_terminal_generation = 0U;
|
||||||
|
portEXIT_CRITICAL(&s_audio_runtime_lock);
|
||||||
|
|
||||||
int retry_count = get_audio_init_retry_count();
|
int retry_count = get_audio_init_retry_count();
|
||||||
esp_err_t last_err = ESP_FAIL;
|
esp_err_t last_err = ESP_FAIL;
|
||||||
|
|
||||||
@@ -1677,6 +1696,26 @@ static void dispatch_downstream_playback_event(
|
|||||||
multimodal_audio_downstream_playback_event_type_t type,
|
multimodal_audio_downstream_playback_event_type_t type,
|
||||||
uint32_t playback_generation)
|
uint32_t playback_generation)
|
||||||
{
|
{
|
||||||
|
bool terminal = type == MULTIMODAL_AUDIO_DOWNSTREAM_PLAYBACK_COMPLETED ||
|
||||||
|
type == MULTIMODAL_AUDIO_DOWNSTREAM_PLAYBACK_STOPPED ||
|
||||||
|
type == MULTIMODAL_AUDIO_DOWNSTREAM_PLAYBACK_STALLED;
|
||||||
|
bool notify_waiter = false;
|
||||||
|
if (terminal) {
|
||||||
|
portENTER_CRITICAL(&s_audio_runtime_lock);
|
||||||
|
s_downstream_last_terminal_valid = true;
|
||||||
|
s_downstream_last_terminal_generation = playback_generation;
|
||||||
|
s_downstream_last_terminal_type = type;
|
||||||
|
if (s_downstream_playback_wait_registered &&
|
||||||
|
s_downstream_playback_wait_generation == playback_generation) {
|
||||||
|
s_downstream_playback_wait_registered = false;
|
||||||
|
notify_waiter = true;
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL(&s_audio_runtime_lock);
|
||||||
|
if (notify_waiter && s_downstream_playback_completion_sem != NULL) {
|
||||||
|
(void)xSemaphoreGive(s_downstream_playback_completion_sem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
multimodal_audio_downstream_playback_callback_t cb = NULL;
|
multimodal_audio_downstream_playback_callback_t cb = NULL;
|
||||||
void *cb_ctx = NULL;
|
void *cb_ctx = NULL;
|
||||||
snapshot_downstream_playback_callback(&cb, &cb_ctx);
|
snapshot_downstream_playback_callback(&cb, &cb_ctx);
|
||||||
@@ -2974,7 +3013,19 @@ esp_err_t multimodal_audio_begin_downstream_response(
|
|||||||
}
|
}
|
||||||
|
|
||||||
TickType_t begin_tick = xTaskGetTickCount();
|
TickType_t begin_tick = xTaskGetTickCount();
|
||||||
|
bool notify_interrupted_waiter = false;
|
||||||
portENTER_CRITICAL(&s_audio_runtime_lock);
|
portENTER_CRITICAL(&s_audio_runtime_lock);
|
||||||
|
if (s_downstream_playback_wait_registered &&
|
||||||
|
s_downstream_playback_wait_generation ==
|
||||||
|
ctx->downstream_playback_generation) {
|
||||||
|
s_downstream_last_terminal_valid = true;
|
||||||
|
s_downstream_last_terminal_generation =
|
||||||
|
ctx->downstream_playback_generation;
|
||||||
|
s_downstream_last_terminal_type =
|
||||||
|
MULTIMODAL_AUDIO_DOWNSTREAM_PLAYBACK_STOPPED;
|
||||||
|
s_downstream_playback_wait_registered = false;
|
||||||
|
notify_interrupted_waiter = true;
|
||||||
|
}
|
||||||
uint32_t playback_generation = advance_downstream_playback_generation_locked();
|
uint32_t playback_generation = advance_downstream_playback_generation_locked();
|
||||||
ctx->downstream_response_active = true;
|
ctx->downstream_response_active = true;
|
||||||
ctx->downstream_response_end_received = false;
|
ctx->downstream_response_end_received = false;
|
||||||
@@ -2983,6 +3034,10 @@ esp_err_t multimodal_audio_begin_downstream_response(
|
|||||||
ctx->downstream_last_audio_tick = begin_tick;
|
ctx->downstream_last_audio_tick = begin_tick;
|
||||||
ctx->downstream_last_response_activity_tick = begin_tick;
|
ctx->downstream_last_response_activity_tick = begin_tick;
|
||||||
portEXIT_CRITICAL(&s_audio_runtime_lock);
|
portEXIT_CRITICAL(&s_audio_runtime_lock);
|
||||||
|
if (notify_interrupted_waiter &&
|
||||||
|
s_downstream_playback_completion_sem != NULL) {
|
||||||
|
(void)xSemaphoreGive(s_downstream_playback_completion_sem);
|
||||||
|
}
|
||||||
*out_playback_generation = playback_generation;
|
*out_playback_generation = playback_generation;
|
||||||
|
|
||||||
s_downstream_queued_packet_total = 0U;
|
s_downstream_queued_packet_total = 0U;
|
||||||
@@ -3055,3 +3110,72 @@ esp_err_t multimodal_audio_finish_downstream_response(void)
|
|||||||
}
|
}
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static esp_err_t downstream_terminal_type_to_error(
|
||||||
|
multimodal_audio_downstream_playback_event_type_t type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case MULTIMODAL_AUDIO_DOWNSTREAM_PLAYBACK_COMPLETED:
|
||||||
|
return ESP_OK;
|
||||||
|
case MULTIMODAL_AUDIO_DOWNSTREAM_PLAYBACK_STALLED:
|
||||||
|
return ESP_ERR_TIMEOUT;
|
||||||
|
case MULTIMODAL_AUDIO_DOWNSTREAM_PLAYBACK_STOPPED:
|
||||||
|
default:
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t multimodal_audio_wait_downstream_playback_complete(
|
||||||
|
uint32_t playback_generation,
|
||||||
|
int timeout_ms)
|
||||||
|
{
|
||||||
|
if (playback_generation == 0U || timeout_ms < 0) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
if (s_downstream_playback_completion_sem == NULL) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove a signal left by a waiter that timed out at a terminal-event race. */
|
||||||
|
(void)xSemaphoreTake(s_downstream_playback_completion_sem, 0U);
|
||||||
|
|
||||||
|
esp_err_t result = ESP_ERR_TIMEOUT;
|
||||||
|
portENTER_CRITICAL(&s_audio_runtime_lock);
|
||||||
|
if (s_downstream_last_terminal_valid &&
|
||||||
|
s_downstream_last_terminal_generation == playback_generation) {
|
||||||
|
result = downstream_terminal_type_to_error(
|
||||||
|
s_downstream_last_terminal_type);
|
||||||
|
portEXIT_CRITICAL(&s_audio_runtime_lock);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (s_audio.downstream_playback_generation != playback_generation) {
|
||||||
|
portEXIT_CRITICAL(&s_audio_runtime_lock);
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
if (s_downstream_playback_wait_registered) {
|
||||||
|
portEXIT_CRITICAL(&s_audio_runtime_lock);
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
s_downstream_playback_wait_registered = true;
|
||||||
|
s_downstream_playback_wait_generation = playback_generation;
|
||||||
|
portEXIT_CRITICAL(&s_audio_runtime_lock);
|
||||||
|
|
||||||
|
TickType_t wait_ticks = pdMS_TO_TICKS((uint32_t)timeout_ms);
|
||||||
|
if (timeout_ms > 0 && wait_ticks == 0U) {
|
||||||
|
wait_ticks = 1U;
|
||||||
|
}
|
||||||
|
(void)xSemaphoreTake(s_downstream_playback_completion_sem, wait_ticks);
|
||||||
|
|
||||||
|
portENTER_CRITICAL(&s_audio_runtime_lock);
|
||||||
|
if (s_downstream_last_terminal_valid &&
|
||||||
|
s_downstream_last_terminal_generation == playback_generation) {
|
||||||
|
result = downstream_terminal_type_to_error(
|
||||||
|
s_downstream_last_terminal_type);
|
||||||
|
}
|
||||||
|
if (s_downstream_playback_wait_registered &&
|
||||||
|
s_downstream_playback_wait_generation == playback_generation) {
|
||||||
|
s_downstream_playback_wait_registered = false;
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL(&s_audio_runtime_lock);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
#define VOICE_PROMPT_FRAME_HEADER_BYTES 4U
|
#define VOICE_PROMPT_FRAME_HEADER_BYTES 4U
|
||||||
#define VOICE_PROMPT_MAX_OPUS_PACKET_BYTES 2048U
|
#define VOICE_PROMPT_MAX_OPUS_PACKET_BYTES 2048U
|
||||||
#define VOICE_PROMPT_OPUS_FRAME_MS 60U
|
#define VOICE_PROMPT_OPUS_FRAME_MS 60U
|
||||||
#define VOICE_PROMPT_DRAIN_MARGIN_MS 120U
|
#define VOICE_PROMPT_COMPLETION_TIMEOUT_MARGIN_MS 3000U
|
||||||
|
|
||||||
static const char *TAG = "power_manager";
|
static const char *TAG = "power_manager";
|
||||||
static const char *VOICE_TAG = "voice_prompt";
|
static const char *VOICE_TAG = "voice_prompt";
|
||||||
@@ -308,11 +308,20 @@ esp_err_t voice_prompt_play_file(const char *path)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (err == ESP_OK) {
|
if (err == ESP_OK) {
|
||||||
uint32_t drain_ms = packet_count * VOICE_PROMPT_OPUS_FRAME_MS +
|
uint64_t timeout_ms =
|
||||||
VOICE_PROMPT_DRAIN_MARGIN_MS;
|
(uint64_t)packet_count * VOICE_PROMPT_OPUS_FRAME_MS +
|
||||||
vTaskDelay(pdMS_TO_TICKS(drain_ms));
|
VOICE_PROMPT_COMPLETION_TIMEOUT_MARGIN_MS;
|
||||||
|
if (timeout_ms > INT32_MAX) {
|
||||||
|
timeout_ms = INT32_MAX;
|
||||||
|
}
|
||||||
|
err = multimodal_audio_wait_downstream_playback_complete(
|
||||||
|
playback_generation,
|
||||||
|
(int)timeout_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err == ESP_OK) {
|
||||||
ESP_LOGI(VOICE_TAG,
|
ESP_LOGI(VOICE_TAG,
|
||||||
"Prompt queued: %s packets=%u bytes=%u",
|
"Prompt completed: %s packets=%u bytes=%u",
|
||||||
resolved_path,
|
resolved_path,
|
||||||
(unsigned)packet_count,
|
(unsigned)packet_count,
|
||||||
(unsigned)total_bytes);
|
(unsigned)total_bytes);
|
||||||
|
|||||||
@@ -356,6 +356,21 @@ menu "Example Configuration"
|
|||||||
Backlight brightness while the LCD render loop continues.
|
Backlight brightness while the LCD render loop continues.
|
||||||
The minimum remains visible so the base expression persists.
|
The minimum remains visible so the base expression persists.
|
||||||
|
|
||||||
|
config TQ_LIGHT_STANDBY_PROMPT_ENABLE
|
||||||
|
bool "Play a prompt before entering light standby"
|
||||||
|
default y
|
||||||
|
depends on TQ_LIGHT_STANDBY_ENABLE
|
||||||
|
help
|
||||||
|
Play the configured prompt to completion before applying
|
||||||
|
standby policies such as disabling the speaker amplifier.
|
||||||
|
|
||||||
|
config TQ_LIGHT_STANDBY_PROMPT_FILE
|
||||||
|
string "Light-standby entry prompt file"
|
||||||
|
default "standby_5min.opus"
|
||||||
|
depends on TQ_LIGHT_STANDBY_PROMPT_ENABLE
|
||||||
|
help
|
||||||
|
Prompt file in the LittleFS audio_prompts directory.
|
||||||
|
|
||||||
config TQ_LIGHT_STANDBY_DISABLE_SPEAKER_OUTPUT
|
config TQ_LIGHT_STANDBY_DISABLE_SPEAKER_OUTPUT
|
||||||
bool "Disable speaker output in light standby"
|
bool "Disable speaker output in light standby"
|
||||||
default y
|
default y
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ CONFIG_TQ_LIGHT_STANDBY_ENABLE=y
|
|||||||
CONFIG_TQ_LIGHT_STANDBY_IDLE_TIMEOUT_MS=300000
|
CONFIG_TQ_LIGHT_STANDBY_IDLE_TIMEOUT_MS=300000
|
||||||
CONFIG_TQ_LIGHT_STANDBY_DIM_BACKLIGHT=y
|
CONFIG_TQ_LIGHT_STANDBY_DIM_BACKLIGHT=y
|
||||||
CONFIG_TQ_LIGHT_STANDBY_BACKLIGHT_PERCENT=10
|
CONFIG_TQ_LIGHT_STANDBY_BACKLIGHT_PERCENT=10
|
||||||
|
CONFIG_TQ_LIGHT_STANDBY_PROMPT_ENABLE=y
|
||||||
|
CONFIG_TQ_LIGHT_STANDBY_PROMPT_FILE="standby_5min.opus"
|
||||||
CONFIG_TQ_LIGHT_STANDBY_DISABLE_SPEAKER_OUTPUT=y
|
CONFIG_TQ_LIGHT_STANDBY_DISABLE_SPEAKER_OUTPUT=y
|
||||||
CONFIG_TQ_LIGHT_STANDBY_RELEASE_MOTOR_HOLD=y
|
CONFIG_TQ_LIGHT_STANDBY_RELEASE_MOTOR_HOLD=y
|
||||||
CONFIG_TQ_LIGHT_STANDBY_WIFI_POWER_SAVE=y
|
CONFIG_TQ_LIGHT_STANDBY_WIFI_POWER_SAVE=y
|
||||||
|
|||||||
Reference in New Issue
Block a user