From 17ac2ce0ed52874616c79c910667e41e34604826 Mon Sep 17 00:00:00 2001
From: wangzhibo <wangzhibo@shsening.com>
Date: 星期一, 31 八月 2026 13:14:12 +0800
Subject: [PATCH] add translation app

---
 xiaozhi-esp32-2.2.6/main/boards/sening-xiaozhi-s3/sening_xiaozhi_s3.cc |   35 ++++++++++++++++++++++++++++++-----
 1 files changed, 30 insertions(+), 5 deletions(-)

diff --git a/xiaozhi-esp32-2.2.6/main/boards/sening-xiaozhi-s3/sening_xiaozhi_s3.cc b/xiaozhi-esp32-2.2.6/main/boards/sening-xiaozhi-s3/sening_xiaozhi_s3.cc
index b7a2ba1..7d087c6 100644
--- a/xiaozhi-esp32-2.2.6/main/boards/sening-xiaozhi-s3/sening_xiaozhi_s3.cc
+++ b/xiaozhi-esp32-2.2.6/main/boards/sening-xiaozhi-s3/sening_xiaozhi_s3.cc
@@ -9,6 +9,7 @@
 #include "power_save_timer.h"
 #include "adc_battery_monitor.h"
 #include "wifi_manager.h"
+#include "app_task.h"
 
 #include <esp_log.h>
 #include <esp_efuse_table.h>
@@ -722,20 +723,27 @@
         static float last_mp3_play_speed = 1.0f;
         static int64_t last_mp3_play_time = 0;
         mcp_server.AddTool("self.mp3.play",
-            "Play one MP3 file from the SD card. The name argument must exactly match a files[].name from self.mp3.get_list. Optional speed supports only 0.5, 0.75, 1.0, 1.25, 1.5, or 2.0; non-1.0 speeds change pitch and affect only SD card MP3 playback. For play, random play, switch song, or named-song requests, call this tool before telling the user playback has started. Do not call this tool repeatedly for the same user request after it has returned accepted=true.",
+            "Play one MP3 file from the SD card. The name argument must exactly match a files[].name from self.mp3.get_list. Optional speed supports only 0.5, 0.75, 1.0, 1.25, 1.5, or 2.0; non-1.0 speeds change pitch and affect only SD card MP3 playback. Optional repeat is the number of full-file loops (default 1). Optional power_off_after requests existing power-off standby after a natural finish, not after stop. For play, random play, switch song, or named-song requests, call this tool before telling the user playback has started. Do not call this tool repeatedly for the same user request after it has returned accepted=true.",
             PropertyList({
                 Property("name", kPropertyTypeString),
-                Property("speed", kPropertyTypeNumber, 1.0)
+                Property("speed", kPropertyTypeNumber, 1.0),
+                Property("repeat", kPropertyTypeInteger, 1, 1, 99),
+                Property("power_off_after", kPropertyTypeBoolean, false)
             }),
             [](const PropertyList& properties) -> ReturnValue {
                 std::string name = properties["name"].value<std::string>();
                 float speed = static_cast<float>(properties["speed"].value<double>());
+                int repeat = properties["repeat"].value<int>();
+                bool power_off_after = properties["power_off_after"].value<bool>();
+                if (repeat < 1) {
+                    repeat = 1;
+                }
                 int64_t now = esp_timer_get_time();
                 if (name == last_mp3_play_name && std::fabs(speed - last_mp3_play_speed) < 0.001f && now - last_mp3_play_time < 15000000) {
                     return std::string("{\"accepted\":true,\"duplicate_ignored\":true}");
                 }
 
-                esp_err_t err = SDCard_Play_MP3_With_Speed(name.data(), speed);
+                esp_err_t err = SDCard_Play_MP3_With_Speed_Repeat(name.data(), speed, repeat, power_off_after);
                 if (err != ESP_OK) {
                     return std::string("{\"accepted\":false,\"error\":\"") + esp_err_to_name(err) +
                         "\",\"retry\":\"use exact files[].name from self.mp3.get_list\"}";
@@ -743,7 +751,8 @@
                 last_mp3_play_name = name;
                 last_mp3_play_speed = speed;
                 last_mp3_play_time = now;
-                return std::string("{\"accepted\":true}");
+                return std::string("{\"accepted\":true,\"repeat\":") + std::to_string(repeat) +
+                    ",\"power_off_after\":" + (power_off_after ? "true" : "false") + "}";
             });
 
         mcp_server.AddTool("self.mp3.get_status",
@@ -751,7 +760,8 @@
             PropertyList(),
             [](const PropertyList& properties) -> ReturnValue {
                 return std::string("{\"playing\":") + (SDCard_Is_Playing() ? "true" : "false") +
-                    ",\"tts_paused\":" + (SDCard_Is_Paused() ? "true" : "false") + "}";
+                    ",\"tts_paused\":" + (SDCard_Is_Paused() ? "true" : "false") +
+                    ",\"repeat_left\":" + std::to_string(SDCard_Get_Repeat_Left()) + "}";
             });
 
         mcp_server.AddTool("self.mp3.stop",
@@ -855,6 +865,21 @@
         InitializeTools();
     }
 
+    void OnDeviceReady() override {
+        xTaskCreate([](void *arg) {
+            (void)arg;
+            vTaskDelay(pdMS_TO_TICKS(1500));
+            Board_ResolveBootOrRestore(false);
+            vTaskDelete(nullptr);
+        }, "boot_app_task", 4096, nullptr, 3, nullptr);
+    }
+
+    bool OnRestoredFromPowerOff(bool network_ready) override {
+        (void)network_ready;
+        Board_ResolveBootOrRestore(true);
+        return true;
+    }
+
     virtual void SetNetworkEventCallback(NetworkEventCallback callback) override {
         WifiBoard::SetNetworkEventCallback([this, callback](NetworkEvent event, const std::string& data) {
             if (event == NetworkEvent::Connected) {

--
Gitblit v1.9.1