Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions agent/brookesia_agent_manager/src/base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ bool Base::set_listening(bool listening)

is_listening_ = listening;

// When the agent returns to listening mode, the downlink speech path is idle.
// Stop the decoder promptly so its feeder/mixer resources do not stay resident
// throughout the entire listen phase.
if (listening && is_decoder_started()) {
stop_audio_decoder();
}

if (!get_attributes().is_general_events_supported(ManagerHelper::AgentGeneralEvent::ListeningStatusChanged)) {
BROOKESIA_LOGD(
"General event '%1%' is not supported, skip",
Expand Down Expand Up @@ -281,6 +288,10 @@ bool Base::feed_audio_decoder_data(const uint8_t *data, size_t data_size)
return true;
}

if (!is_decoder_started()) {
BROOKESIA_CHECK_FALSE_RETURN(start_audio_decoder(), false, "Failed to lazily start audio decoder");
}

auto result = AudioHelper::call_function_sync(
AudioHelper::FunctionId::FeedDecoderData, service::RawBuffer(data, data_size)
);
Expand Down Expand Up @@ -720,9 +731,10 @@ bool Base::start_audio_decoder()
}

auto &decoder_config = get_audio_config().decoder;
AudioHelper::call_function_async(
AudioHelper::FunctionId::StartDecoder, BROOKESIA_DESCRIBE_TO_JSON(decoder_config).as_object()
);
auto result = AudioHelper::call_function_sync(
AudioHelper::FunctionId::StartDecoder, BROOKESIA_DESCRIBE_TO_JSON(decoder_config).as_object()
);
BROOKESIA_CHECK_FALSE_RETURN(result, false, "Failed to start decoder: %1%", result.error());

is_decoder_started_ = true;

Expand All @@ -738,7 +750,11 @@ void Base::stop_audio_decoder()
return;
}

AudioHelper::call_function_async(AudioHelper::FunctionId::StopDecoder);
auto result = AudioHelper::call_function_sync(AudioHelper::FunctionId::StopDecoder);
if (!result) {
BROOKESIA_LOGE("Failed to stop decoder: %1%", result.error());
return;
}
is_decoder_started_ = false;
}

Expand Down
14 changes: 9 additions & 5 deletions agent/brookesia_agent_xiaozhi/xiaozhi/src/esp_xiaozhi_vision.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,11 @@ esp_err_t esp_xiaozhi_vision_explain_jpeg(const esp_xiaozhi_vision_t *vision,
const size_t footer_len = boundary_len + 8;
const size_t body_len = part1_len + part2_len + jpeg_len + footer_len;

uint8_t *body = (uint8_t *)heap_caps_malloc(body_len, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
#if CONFIG_SPIRAM_BOOT_INIT
uint8_t *body = (uint8_t *)heap_caps_malloc(body_len, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
#else
uint8_t *body = (uint8_t *)malloc(body_len);
#endif
ESP_RETURN_ON_FALSE(body != NULL, ESP_ERR_NO_MEM, log_tag, "Failed to allocate HTTP body");

uint8_t *cursor = body;
Expand All @@ -130,7 +134,7 @@ esp_err_t esp_xiaozhi_vision_explain_jpeg(const esp_xiaozhi_vision_t *vision,
"%s\r\n",
ESP_XIAOZHI_VISION_BOUNDARY, question);
if (written < 0 || (size_t)written != part1_len) {
heap_caps_free(body);
free(body);
return ESP_FAIL;
}
cursor += part1_len;
Expand All @@ -142,7 +146,7 @@ esp_err_t esp_xiaozhi_vision_explain_jpeg(const esp_xiaozhi_vision_t *vision,
"\r\n",
ESP_XIAOZHI_VISION_BOUNDARY, filename);
if (written < 0 || (size_t)written != part2_len) {
heap_caps_free(body);
free(body);
return ESP_FAIL;
}
cursor += part2_len;
Expand Down Expand Up @@ -178,7 +182,7 @@ esp_err_t esp_xiaozhi_vision_explain_jpeg(const esp_xiaozhi_vision_t *vision,

esp_http_client_handle_t client = esp_http_client_init(&config);
if (client == NULL) {
heap_caps_free(body);
free(body);
return ESP_FAIL;
}

Expand All @@ -201,7 +205,7 @@ esp_err_t esp_xiaozhi_vision_explain_jpeg(const esp_xiaozhi_vision_t *vision,
}

esp_http_client_cleanup(client);
heap_caps_free(body);
free(body);

return ret;
}
8 changes: 6 additions & 2 deletions examples/agent/chatbot/main/modules/display/display.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,11 @@ bool Display::start_lvgl(int core_id)
return false;
}
#pragma GCC diagnostic pop
// Avoid the full-height double-buffer preset on SPI panels. It is memory-hungry
// and can starve the audio pipeline when PSRAM becomes fragmented.
display_config.profile.use_psram = true;
display_config.profile.require_double_buffer = true;
display_config.profile.buffer_height = std::min<uint16_t>(50, static_cast<uint16_t>(display_info.v_res));
display_config.profile.require_double_buffer = false;

lv_display_t *lv_display = esp_lv_adapter_register_display(&display_config);
if (!lv_display) {
Expand Down Expand Up @@ -314,8 +317,9 @@ bool Display::start_expression_emote(int core_id)
.task_affinity = core_id,
.task_stack_in_ext = true,
.flag_swap_color_bytes = (driver_specific.bus_type == hal::DisplayPanelIface::BusType::Generic),
.flag_double_buffer = true,
.flag_double_buffer = false,
.flag_buff_dma = true,
.flag_buff_spiram = true,
};
auto result = EmoteHelper::call_function_sync(
EmoteHelper::FunctionId::SetConfig, BROOKESIA_DESCRIBE_TO_JSON(config).as_object()
Expand Down
30 changes: 26 additions & 4 deletions service/brookesia_service_audio/src/service_audio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1057,11 +1057,33 @@ bool Audio::start_encoder(const AudioEncoderDynamicConfig &config)
#endif

auto recorder_handle = TypeConverter::to_recorder_handle(recorder_handle_);
lib_utils::FunctionGuard close_recorder_guard([this, recorder_handle]() {
auto close_recorder_safely = [this, recorder_handle]() {
BROOKESIA_LOG_TRACE_GUARD_WITH_THIS();
BROOKESIA_CHECK_ESP_ERR_EXECUTE(audio_recorder_close(recorder_handle), {}, {
BROOKESIA_LOGE("Failed to close recorder");
});
#if (BROOKESIA_SERVICE_AUDIO_ENABLE_WORKER && !BROOKESIA_SERVICE_AUDIO_WORKER_STACK_IN_EXT) || \
!BROOKESIA_SERVICE_MANAGER_WORKER_STACK_IN_EXT
BROOKESIA_CHECK_ESP_ERR_RETURN(audio_recorder_close(recorder_handle), false, "Failed to close recorder");
#else
auto current_thread_config = BROOKESIA_THREAD_GET_CURRENT_CONFIG();
if (!current_thread_config.stack_in_ext) {
BROOKESIA_CHECK_ESP_ERR_RETURN(audio_recorder_close(recorder_handle), false, "Failed to close recorder");
} else {
// Closing the recorder unmaps SR models from flash, so use an internal-memory stack.
BROOKESIA_THREAD_CONFIG_GUARD({
.stack_in_ext = false,
});
auto recorder_close_future = std::async(std::launch::async, [this, recorder_handle]() mutable {
BROOKESIA_LOG_TRACE_GUARD_WITH_THIS();
BROOKESIA_CHECK_ESP_ERR_RETURN(audio_recorder_close(recorder_handle), false, "Failed to close recorder");
return true;
});
BROOKESIA_CHECK_FALSE_RETURN(recorder_close_future.get(), false, "Failed to close recorder");
}
#endif
return true;
};
lib_utils::FunctionGuard close_recorder_guard([this, close_recorder_safely]() {
BROOKESIA_LOG_TRACE_GUARD_WITH_THIS();
BROOKESIA_CHECK_FALSE_EXIT(close_recorder_safely(), "Failed to close recorder");
});

auto recorder_fetch_thread_func =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class Audio: public Base<Audio> {
.core_id = 0,
.priority = 5,
.stack_size = 4 * 1024,
.stack_in_ext = true,
.stack_in_ext = false,
};
MixerGainConfig mixer_gain{
.initial_gain = 0.6,
Expand Down Expand Up @@ -105,7 +105,7 @@ class Audio: public Base<Audio> {
.core_id = 1,
.priority = 12,
.stack_size = 6 * 1024,
.stack_in_ext = true,
.stack_in_ext = false,
};
};
struct EncoderExtraConfigOpus {
Expand Down Expand Up @@ -166,7 +166,7 @@ class Audio: public Base<Audio> {
.core_id = 0,
.priority = 5,
.stack_size = 6 * 1024,
.stack_in_ext = true,
.stack_in_ext = false,
};
std::optional<AFE_VAD_Config> vad = std::nullopt;
std::optional<AFE_WakeNetConfig> wakenet = std::nullopt;
Expand Down