Browse Source

feat(tts): 新增TTS语速调节功能并支持多语言翻译

为TTS功能添加语速调节支持,包括:
1. 新增语速设置接口和实现
2. 添加多语言翻译文本
3. 实现语速设置持久化存储
4. 在UI中添加语速调节对话框
5. 支持Android/iOS平台语速设置同步
newdev_shunjiawei
lxm 9 months ago
parent
commit
8f76ede475
  1. 2
      lib/core/translations/language/ar_sa.dart
  2. 2
      lib/core/translations/language/bg_bg.dart
  3. 2
      lib/core/translations/language/cs_cz.dart
  4. 2
      lib/core/translations/language/da_dk.dart
  5. 2
      lib/core/translations/language/de_de.dart
  6. 2
      lib/core/translations/language/el_gr.dart
  7. 2
      lib/core/translations/language/en_us.dart
  8. 2
      lib/core/translations/language/es_es.dart
  9. 2
      lib/core/translations/language/et_ee.dart
  10. 2
      lib/core/translations/language/fa_ir.dart
  11. 2
      lib/core/translations/language/fi_fi.dart
  12. 2
      lib/core/translations/language/fr_fr.dart
  13. 2
      lib/core/translations/language/hi_in.dart
  14. 2
      lib/core/translations/language/hr_hr.dart
  15. 2
      lib/core/translations/language/hu_hu.dart
  16. 2
      lib/core/translations/language/id_id.dart
  17. 2
      lib/core/translations/language/is_is.dart
  18. 2
      lib/core/translations/language/it_it.dart
  19. 2
      lib/core/translations/language/ja_jp.dart
  20. 2
      lib/core/translations/language/ko_kr.dart
  21. 2
      lib/core/translations/language/lt_lt.dart
  22. 2
      lib/core/translations/language/lv_lv.dart
  23. 2
      lib/core/translations/language/ms_my.dart
  24. 2
      lib/core/translations/language/nb_no.dart
  25. 2
      lib/core/translations/language/nl_nl.dart
  26. 2
      lib/core/translations/language/pl_pl.dart
  27. 2
      lib/core/translations/language/pt_pt.dart
  28. 2
      lib/core/translations/language/ro_ro.dart
  29. 2
      lib/core/translations/language/ru_ru.dart
  30. 2
      lib/core/translations/language/sk_sk.dart
  31. 2
      lib/core/translations/language/sl_si.dart
  32. 2
      lib/core/translations/language/sr_rs.dart
  33. 2
      lib/core/translations/language/sv_se.dart
  34. 2
      lib/core/translations/language/th_th.dart
  35. 2
      lib/core/translations/language/tl_ph.dart
  36. 2
      lib/core/translations/language/tr_tr.dart
  37. 2
      lib/core/translations/language/uk_ua.dart
  38. 2
      lib/core/translations/language/vi_vn.dart
  39. 2
      lib/core/translations/language/zh_cn.dart
  40. 2
      lib/core/translations/language/zh_hk.dart
  41. 2
      lib/core/translations/language/zh_tw.dart
  42. 49
      lib/modules/agent/controllers/agent_controller.dart
  43. 118
      lib/modules/agent/views/agent_view.dart
  44. 48
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt
  45. 15
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentServicePlugin.kt
  46. 28
      local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift
  47. 8
      local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServicePlugin.swift
  48. 15
      local_plugins/agent_service/lib/agent_service.dart

2
lib/core/translations/language/ar_sa.dart

@ -718,6 +718,8 @@ const Map<String, String> arSA = {
"Lily": "Lily",
"clearChatHistory": "مسح سجل الدردشة",
"online": "متصل",
"ttsSpeed": "سرعة الكلام",
"ttsSpeedDefaultHint": "الإعداد الافتراضي +25٪",
"inputMessage": "أدخل رسالة...",
"imListening": "أنا أستمع...",
"canStartTalking": "يمكنك بدء التحدث",

2
lib/core/translations/language/bg_bg.dart

@ -731,6 +731,8 @@ const Map<String, String> bgBG = {
"Lily": "Lily",
"clearChatHistory": "Изчисти историята на чата",
"online": "Онлайн",
"ttsSpeed": "Скорост на речта",
"ttsSpeedDefaultHint": "По подразбиране +25%",
"inputMessage": "Въведете съобщение...",
"imListening": "Слушам...",
"canStartTalking": "Можете да започнете да говорите",

2
lib/core/translations/language/cs_cz.dart

@ -721,6 +721,8 @@ const Map<String, String> csCZ = {
"Lily": "Lily",
"clearChatHistory": "Vymazat historii chatu",
"online": "Online",
"ttsSpeed": "Rychlost řeči",
"ttsSpeedDefaultHint": "Výchozí +25 %",
"inputMessage": "Zadejte zprávu...",
"imListening": "Poslouchám...",
"canStartTalking": "Můžete začít mluvit",

2
lib/core/translations/language/da_dk.dart

@ -726,6 +726,8 @@ const Map<String, String> daDK = {
"Lily": "Lily",
"clearChatHistory": "Ryd chathistorik",
"online": "Online",
"ttsSpeed": "Talefart",
"ttsSpeedDefaultHint": "Standard +25%",
"inputMessage": "Indtast besked...",
"imListening": "Lytter...",
"canStartTalking": "Du kan begynde at tale",

2
lib/core/translations/language/de_de.dart

@ -735,6 +735,8 @@ const Map<String, String> deDE = {
"Lily": "Lily",
"clearChatHistory": "Chat-Verlauf löschen",
"online": "Online",
"ttsSpeed": "Sprachgeschwindigkeit",
"ttsSpeedDefaultHint": "Standard +25 %",
"inputMessage": "Nachricht eingeben...",
"imListening": "Ich höre zu...",
"canStartTalking": "Sie können anfangen zu sprechen",

2
lib/core/translations/language/el_gr.dart

@ -740,6 +740,8 @@ const Map<String, String> elGR = {
"Lily": "Lily",
"clearChatHistory": "Εκκαθάριση ιστορικού συνομιλίας",
"online": "Σε σύνδεση",
"ttsSpeed": "Ταχύτητα ομιλίας",
"ttsSpeedDefaultHint": "Προεπιλογή +25%",
"inputMessage": "Εισάγετε μήνυμα...",
"imListening": "Ακούω...",
"canStartTalking": "Μπορείτε να αρχίσετε να μιλάτε",

2
lib/core/translations/language/en_us.dart

@ -722,6 +722,8 @@ const Map<String, String> enUS = {
"Lily": "Lily",
"clearChatHistory": "Clear Chat History",
"online": "Online",
"ttsSpeed": "Speech speed",
"ttsSpeedDefaultHint": "Default +25%",
"inputMessage": "Type a message...",
"imListening": "Listening...",
"canStartTalking": "You may start speaking",

2
lib/core/translations/language/es_es.dart

@ -725,6 +725,8 @@ const Map<String, String> esES = {
"Lily": "Lily",
"clearChatHistory": "Limpiar historial de chat",
"online": "En línea",
"ttsSpeed": "Velocidad de voz",
"ttsSpeedDefaultHint": "Predeterminado +25%",
"inputMessage": "Ingresa mensaje...",
"imListening": "Estoy escuchando...",
"canStartTalking": "Puedes empezar a hablar",

2
lib/core/translations/language/et_ee.dart

@ -718,6 +718,8 @@ const Map<String, String> etEE = {
"Lily": "Lily",
"clearChatHistory": "Tühjenda vestluse ajalugu",
"online": "Võrgus",
"ttsSpeed": "Kõnekiirus",
"ttsSpeedDefaultHint": "Vaikimisi +25%",
"inputMessage": "Sisesta sõnum...",
"imListening": "Kuulan...",
"canStartTalking": "Võid hakata rääkima",

2
lib/core/translations/language/fa_ir.dart

@ -720,6 +720,8 @@ const Map<String, String> faIR = {
"Lily": "Lily",
"clearChatHistory": "پاک کردن تاریخچه چت",
"online": "آنلاین",
"ttsSpeed": "سرعت گفتار",
"ttsSpeedDefaultHint": "پیش‌فرض +25٪",
"inputMessage": "پیامی تایپ کنید...",
"imListening": "در حال شنیدن...",
"canStartTalking": "می‌توانید شروع به صحبت کنید",

2
lib/core/translations/language/fi_fi.dart

@ -725,6 +725,8 @@ const Map<String, String> fiFI = {
"Lily": "Lily",
"clearChatHistory": "Tyhjennä keskusteluhistoria",
"online": "Online",
"ttsSpeed": "Puhenopeus",
"ttsSpeedDefaultHint": "Oletus +25 %",
"inputMessage": "Kirjoita viesti...",
"imListening": "Kuuntelen...",
"canStartTalking": "Voit aloittaa puhumisen",

2
lib/core/translations/language/fr_fr.dart

@ -727,6 +727,8 @@ const Map<String, String> frFR = {
"Lily": "Lily",
"clearChatHistory": "Effacer l'historique de chat",
"online": "En ligne",
"ttsSpeed": "Vitesse de la voix",
"ttsSpeedDefaultHint": "Par défaut +25%",
"inputMessage": "Saisir un message...",
"imListening": "J'écoute...",
"canStartTalking": "Vous pouvez commencer à parler",

2
lib/core/translations/language/hi_in.dart

@ -721,6 +721,8 @@ const Map<String, String> hiIN = {
"Lily": "Lily",
"clearChatHistory": "चैट इतिहास साफ़ करें",
"online": "ऑनलाइन",
"ttsSpeed": "आवाज़ की गति",
"ttsSpeedDefaultHint": "डिफ़ॉल्ट +25%",
"inputMessage": "संदेश दर्ज करें...",
"imListening": "सुन रहा हूँ...",
"canStartTalking": "आप बात करना शुरू कर सकते हैं",

2
lib/core/translations/language/hr_hr.dart

@ -726,6 +726,8 @@ const Map<String, String> hrHR = {
"Lily": "Lily",
"clearChatHistory": "Obriši povijest razgovora",
"online": "Na mreži",
"ttsSpeed": "Brzina govora",
"ttsSpeedDefaultHint": "Zadano +25%",
"inputMessage": "Unesite poruku...",
"imListening": "Slušam...",
"canStartTalking": "Možete početi govoriti",

2
lib/core/translations/language/hu_hu.dart

@ -727,6 +727,8 @@ const Map<String, String> huHU = {
"Lily": "Lily",
"clearChatHistory": "Csevegési előzmények törlése",
"online": "Online",
"ttsSpeed": "Beszédsebesség",
"ttsSpeedDefaultHint": "Alapértelmezett +25%",
"inputMessage": "Üzenet beírása...",
"imListening": "Hallgatom...",
"canStartTalking": "Elkezdhet beszélni",

2
lib/core/translations/language/id_id.dart

@ -721,6 +721,8 @@ const Map<String, String> idID = {
"Lily": "Lily",
"clearChatHistory": "Hapus Riwayat Obrolan",
"online": "Online",
"ttsSpeed": "Kecepatan suara",
"ttsSpeedDefaultHint": "Bawaan +25%",
"inputMessage": "Masukkan pesan...",
"imListening": "Mendengarkan...",
"canStartTalking": "Anda bisa mulai berbicara",

2
lib/core/translations/language/is_is.dart

@ -723,6 +723,8 @@ const Map<String, String> isIS = {
"Lily": "Lily",
"clearChatHistory": "Hreinsa spjallsögu",
"online": "Á netinu",
"ttsSpeed": "Talhraði",
"ttsSpeedDefaultHint": "Sjálfgefið +25%",
"inputMessage": "Sláðu inn skilaboð...",
"imListening": "Hlusta...",
"canStartTalking": "Þú getur byrjað að tala",

2
lib/core/translations/language/it_it.dart

@ -732,6 +732,8 @@ const Map<String, String> itIT = {
"Lily": "Lily",
"clearChatHistory": "Cancella cronologia chat",
"online": "Online",
"ttsSpeed": "Velocità della voce",
"ttsSpeedDefaultHint": "Predefinito +25%",
"inputMessage": "Inserisci messaggio...",
"imListening": "Sto ascoltando...",
"canStartTalking": "Puoi iniziare a parlare",

2
lib/core/translations/language/ja_jp.dart

@ -711,6 +711,8 @@ const Map<String, String> jaJP = {
"Lily": "Lily",
"clearChatHistory": "チャット履歴をクリア",
"online": "オンライン",
"ttsSpeed": "話速",
"ttsSpeedDefaultHint": "デフォルト +25%",
"inputMessage": "メッセージを入力...",
"imListening": "聞いています...",
"canStartTalking": "話し始めることができます",

2
lib/core/translations/language/ko_kr.dart

@ -707,6 +707,8 @@ const Map<String, String> koKR = {
"Lily": "Lily",
"clearChatHistory": "채팅 기록 지우기",
"online": "온라인",
"ttsSpeed": "음성 속도",
"ttsSpeedDefaultHint": "기본값 +25%",
"inputMessage": "메시지 입력...",
"imListening": "듣고 있습니다...",
"canStartTalking": "말씀을 시작할 수 있습니다",

2
lib/core/translations/language/lt_lt.dart

@ -727,6 +727,8 @@ const Map<String, String> ltLT = {
"Lily": "Lily",
"clearChatHistory": "Išvalyti pokalbių istoriją",
"online": "Prisijungęs",
"ttsSpeed": "Kalbėjimo greitis",
"ttsSpeedDefaultHint": "Numatytasis +25%",
"inputMessage": "Įveskite žinutę...",
"imListening": "Klausausi...",
"canStartTalking": "Galite pradėti kalbėti",

2
lib/core/translations/language/lv_lv.dart

@ -726,6 +726,8 @@ const Map<String, String> lvLV = {
"Lily": "Lily",
"clearChatHistory": "Notīrīt tērzēšanas vēsturi",
"online": "Tiešsaistē",
"ttsSpeed": "Runas ātrums",
"ttsSpeedDefaultHint": "Noklusējums +25%",
"inputMessage": "Ievadiet ziņu...",
"imListening": "Klausos...",
"canStartTalking": "Jūs varat sākt runāt",

2
lib/core/translations/language/ms_my.dart

@ -723,6 +723,8 @@ const Map<String, String> msMY = {
"Lily": "Lily",
"clearChatHistory": "Kosongkan Sejarah Sembang",
"online": "Dalam Talian",
"ttsSpeed": "Kelajuan suara",
"ttsSpeedDefaultHint": "Lalai +25%",
"inputMessage": "Masukkan mesej...",
"imListening": "Mendengar...",
"canStartTalking": "Anda boleh mula bercakap",

2
lib/core/translations/language/nb_no.dart

@ -725,6 +725,8 @@ const Map<String, String> nbNO = {
"Lily": "Lily",
"clearChatHistory": "Tøm chat-historikk",
"online": "Online",
"ttsSpeed": "Talefart",
"ttsSpeedDefaultHint": "Standard +25 %",
"inputMessage": "Skriv melding...",
"imListening": "Lytter...",
"canStartTalking": "Du kan begynne å snakke",

2
lib/core/translations/language/nl_nl.dart

@ -724,6 +724,8 @@ const Map<String, String> nlNL = {
"Lily": "Lily",
"clearChatHistory": "Chatgeschiedenis wissen",
"online": "Online",
"ttsSpeed": "Spreeksnelheid",
"ttsSpeedDefaultHint": "Standaard +25%",
"inputMessage": "Voer bericht in...",
"imListening": "Ik luister...",
"canStartTalking": "U kunt beginnen met praten",

2
lib/core/translations/language/pl_pl.dart

@ -727,6 +727,8 @@ const Map<String, String> plPL = {
"Lily": "Lily",
"clearChatHistory": "Wyczyść historię czatu",
"online": "Online",
"ttsSpeed": "Prędkość mowy",
"ttsSpeedDefaultHint": "Domyślnie +25%",
"inputMessage": "Wpisz wiadomość...",
"imListening": "Słucham...",
"canStartTalking": "Możesz zacząć mówić",

2
lib/core/translations/language/pt_pt.dart

@ -731,6 +731,8 @@ const Map<String, String> ptPT = {
"Lily": "Lily",
"clearChatHistory": "Limpar Histórico de Chat",
"online": "Online",
"ttsSpeed": "Velocidade da voz",
"ttsSpeedDefaultHint": "Padrão +25%",
"inputMessage": "Introduzir mensagem...",
"imListening": "A ouvir...",
"canStartTalking": "Pode começar a falar",

2
lib/core/translations/language/ro_ro.dart

@ -727,6 +727,8 @@ const Map<String, String> roRO = {
"Lily": "Lily",
"clearChatHistory": "Șterge istoricul chatului",
"online": "Online",
"ttsSpeed": "Viteza vocii",
"ttsSpeedDefaultHint": "Implicit +25%",
"inputMessage": "Introduceți mesaj...",
"imListening": "Ascult...",
"canStartTalking": "Puteți începe să vorbiți",

2
lib/core/translations/language/ru_ru.dart

@ -727,6 +727,8 @@ const Map<String, String> ruRU = {
"Lily": "Lily",
"clearChatHistory": "Очистить историю чата",
"online": "В сети",
"ttsSpeed": "Скорость речи",
"ttsSpeedDefaultHint": "По умолчанию +25%",
"inputMessage": "Введите сообщение...",
"imListening": "Слушаю...",
"canStartTalking": "Можете начинать говорить",

2
lib/core/translations/language/sk_sk.dart

@ -724,6 +724,8 @@ const Map<String, String> skSK = {
"Lily": "Lily",
"clearChatHistory": "Vymazať históriu chatu",
"online": "Online",
"ttsSpeed": "Rýchlosť reči",
"ttsSpeedDefaultHint": "Predvolené +25%",
"inputMessage": "Zadajte správu...",
"imListening": "Počúvam...",
"canStartTalking": "Môžete začať hovoriť",

2
lib/core/translations/language/sl_si.dart

@ -726,6 +726,8 @@ const Map<String, String> slSI = {
"Lily": "Lily",
"clearChatHistory": "Počisti zgodovino klepeta",
"online": "Na spletu",
"ttsSpeed": "Hitrost govora",
"ttsSpeedDefaultHint": "Privzeto +25%",
"inputMessage": "Vnesite sporočilo...",
"imListening": "Poslušam...",
"canStartTalking": "Lahko začnete govoriti",

2
lib/core/translations/language/sr_rs.dart

@ -724,6 +724,8 @@ const Map<String, String> srRS = {
"Lily": "Lily",
"clearChatHistory": "Obriši istoriju ćaskanja",
"online": "Na mreži",
"ttsSpeed": "Brzina govora",
"ttsSpeedDefaultHint": "Podrazumevano +25%",
"inputMessage": "Unesite poruku...",
"imListening": "Slušam...",
"canStartTalking": "Možete početi da govorite",

2
lib/core/translations/language/sv_se.dart

@ -724,6 +724,8 @@ const Map<String, String> svSE = {
"Lily": "Lily",
"clearChatHistory": "Rensa chatthistorik",
"online": "Online",
"ttsSpeed": "Talthastighet",
"ttsSpeedDefaultHint": "Standard +25%",
"inputMessage": "Skriv meddelande...",
"imListening": "Lyssnar...",
"canStartTalking": "Du kan börja prata",

2
lib/core/translations/language/th_th.dart

@ -718,6 +718,8 @@ const Map<String, String> thTH = {
"Lily": "Lily",
"clearChatHistory": "ล้างประวัติการแชท",
"online": "ออนไลน์",
"ttsSpeed": "ความเร็วคำพูด",
"ttsSpeedDefaultHint": "ค่าเริ่มต้น +25%",
"inputMessage": "ป้อนข้อความ...",
"imListening": "กำลังฟัง...",
"canStartTalking": "คุณสามารถเริ่มพูดได้",

2
lib/core/translations/language/tl_ph.dart

@ -728,6 +728,8 @@ const Map<String, String> tlPH = {
"Lily": "Lily",
"clearChatHistory": "Burahin ang Kasaysayan ng Chat",
"online": "Online",
"ttsSpeed": "Bilis ng pagsasalita",
"ttsSpeedDefaultHint": "Default +25%",
"inputMessage": "Mag-type ng mensahe...",
"imListening": "Nakikinig...",
"canStartTalking": "Maaari ka nang magsalita",

2
lib/core/translations/language/tr_tr.dart

@ -723,6 +723,8 @@ const Map<String, String> trTR = {
"Lily": "Lily",
"clearChatHistory": "Sohbet Geçmişini Temizle",
"online": "Çevrimiçi",
"ttsSpeed": "Konuşma hızı",
"ttsSpeedDefaultHint": "Varsayılan +25%",
"inputMessage": "Mesaj girin...",
"imListening": "Dinliyorum...",
"canStartTalking": "Konuşmaya başlayabilirsiniz",

2
lib/core/translations/language/uk_ua.dart

@ -724,6 +724,8 @@ const Map<String, String> ukUA = {
"Lily": "Lily",
"clearChatHistory": "Очистити історію чату",
"online": "В мережі",
"ttsSpeed": "Швидкість мовлення",
"ttsSpeedDefaultHint": "Типово +25%",
"inputMessage": "Введіть повідомлення...",
"imListening": "Слухаю...",
"canStartTalking": "Можете починати говорити",

2
lib/core/translations/language/vi_vn.dart

@ -720,6 +720,8 @@ const Map<String, String> viVN = {
"Lily": "Lily",
"clearChatHistory": "Xóa lịch sử trò chuyện",
"online": "Trực tuyến",
"ttsSpeed": "Tốc độ giọng nói",
"ttsSpeedDefaultHint": "Mặc định +25%",
"inputMessage": "Nhập tin nhắn...",
"imListening": "Đang lắng nghe...",
"canStartTalking": "Bạn có thể bắt đầu nói",

2
lib/core/translations/language/zh_cn.dart

@ -676,6 +676,8 @@ const Map<String, String> zhCN = {
"Lily": "Lily",
"clearChatHistory": "清空聊天记录",
"online": "在线",
"ttsSpeed": "语速",
"ttsSpeedDefaultHint": "默认 +25%",
"inputMessage": "输入消息...",
"imListening": "正在聆听...",
"canStartTalking": "你可以开始说话",

2
lib/core/translations/language/zh_hk.dart

@ -677,6 +677,8 @@ const Map<String, String> zhHK = {
"Lily": "Lily",
"clearChatHistory": "清空聊天記錄",
"online": "在線",
"ttsSpeed": "語速",
"ttsSpeedDefaultHint": "預設 +25%",
"inputMessage": "輸入訊息...",
"imListening": "正在聆聽...",
"canStartTalking": "你可以開始說話",

2
lib/core/translations/language/zh_tw.dart

@ -677,6 +677,8 @@ const Map<String, String> zhTW = {
"Lily": "Lily",
"clearChatHistory": "清空聊天記錄",
"online": "在線",
"ttsSpeed": "語速",
"ttsSpeedDefaultHint": "預設 +25%",
"inputMessage": "輸入訊息...",
"imListening": "正在聆聽...",
"canStartTalking": "你可以開始說話",

49
lib/modules/agent/controllers/agent_controller.dart

@ -81,6 +81,7 @@ class AgentController extends GetxController with WidgetsBindingObserver {
final isProcessing = false.obs;
// TTS启用状态
final isTtsEnabled = true.obs;
final ttsRatePercent = 25.obs;
// TTS播报打断启用状态
final isTtsInterruptEnabled = false.obs;
// 是否显示工具调用
@ -179,6 +180,7 @@ class AgentController extends GetxController with WidgetsBindingObserver {
});
_initAppDir();
_loadTtsPreference(); //加载TTS偏好设置
_loadTtsRatePreference();
_loadTtsInterrupe(); //加载TTS播报打断偏好设置
// 监听存储中的“agent_tts_interrupe”键变化,实现跨页面状态同步
// 中文注释:当 Home 页或 Agent 页任意一处切换时,另一处可以通过监听存储自动刷新 UI
@ -269,6 +271,30 @@ class AgentController extends GetxController with WidgetsBindingObserver {
}
}
Future<void> _loadTtsRatePreference() async {
try {
final value = _storage.read('agent_tts_rate_percent');
if (value is int) {
ttsRatePercent.value = value;
} else if (value is String) {
final parsed = int.tryParse(value);
if (parsed != null) {
ttsRatePercent.value = parsed;
}
} else {
ttsRatePercent.value = 25;
}
} catch (e) {
ttsRatePercent.value = 25;
}
try {
await AgentService.setTtsRate(ttsRatePercent.value);
} catch (e) {
Logger.e(TAG, '同步TTS语速失败: $e');
}
}
// 切换TTS状态
Future<void> toggleTts() async {
isTtsEnabled.toggle();
@ -287,6 +313,29 @@ class AgentController extends GetxController with WidgetsBindingObserver {
}
}
Future<void> updateTtsRate(int rate) async {
final newRate = rate.clamp(0, 100);
ttsRatePercent.value = newRate;
if (isSpeaking.value) {
try {
await AgentService.stopTts();
} catch (_) {}
}
try {
await AgentService.setTtsRate(newRate);
} catch (e) {
Logger.e(TAG, '设置TTS语速失败: $e');
}
try {
await _storage.write('agent_tts_rate_percent', newRate);
} catch (e) {
Logger.e(TAG, '保存TTS语速偏好设置失败: $e');
}
}
// 加载TTS偏好设置
Future<void> _loadTtsInterrupe() async {
try {

118
lib/modules/agent/views/agent_view.dart

@ -29,6 +29,77 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver {
// 👇 新增:用于控制 TextField 焦点
final FocusNode _textFocusNode = FocusNode();
void _showTtsSpeedDialog() {
final primaryColor = Theme.of(context).primaryColor;
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
final RxInt tempRate = controller.ttsRatePercent.value.obs;
Get.dialog(
AlertDialog(
backgroundColor: isDarkMode ? Colors.grey[850] : Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.w),
),
title: Text(
'ttsSpeed'.tr, // 语速
style: TextStyle(fontSize: 16.sp),
),
content: Obx(() {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
'ttsSpeedDefaultHint'.tr, // 默认 +25%
style: TextStyle(
fontSize: 12.sp,
color: isDarkMode ? Colors.grey[400] : Colors.grey[600],
),
),
),
SizedBox(height: 12.h),
Text(
'+${tempRate.value}%',
style: TextStyle(
fontSize: 20.sp,
fontWeight: FontWeight.w600,
color: primaryColor,
),
),
SizedBox(height: 8.h),
Slider(
value: tempRate.value.toDouble(),
min: 0,
max: 100,
divisions: 20,
label: '+${tempRate.value}%',
activeColor: primaryColor,
onChanged: (value) {
tempRate.value = value.round();
},
),
],
);
}),
actions: [
TextButton(
onPressed: () => Get.back(),
child: Text('cancel'.tr),
),
TextButton(
onPressed: () async {
Get.back();
await controller.updateTtsRate(tempRate.value);
},
child: Text('confirm'.tr),
),
],
),
barrierDismissible: true,
);
}
@override
void initState() {
super.initState();
@ -86,7 +157,7 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver {
borderRadius: BorderRadius.circular(20.w),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
color: Colors.black.withValues(alpha: 0.1),
blurRadius: 3.w,
offset: const Offset(0, 1),
),
@ -179,13 +250,14 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver {
height: 34.w,
margin: EdgeInsets.only(right: 8.w),
decoration: BoxDecoration(
color:
isDarkMode ? Colors.white.withOpacity(0.1) : Colors.grey[100],
color: isDarkMode
? Colors.white.withValues(alpha: 0.1)
: Colors.grey[100],
shape: BoxShape.circle,
boxShadow: [
if (!isDarkMode)
BoxShadow(
color: Colors.black.withOpacity(0.05),
color: Colors.black.withValues(alpha: 0.05),
blurRadius: 2.w,
spreadRadius: 0.5.w,
),
@ -268,6 +340,21 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver {
onTap: () => controller.toggleTtsInterrupt(),
),
PopupMenuItem(
child: Row(
children: [
Icon(Icons.speed, color: primaryColor, size: 20.sp),
SizedBox(width: 8.w),
Text('ttsSpeed'.tr, // 语速
style: TextStyle(fontSize: 14.sp)),
],
),
onTap: () => Future.delayed(
const Duration(milliseconds: 100),
() => _showTtsSpeedDialog(),
),
),
// 清除聊天记录选项
PopupMenuItem(
child: Row(
@ -448,14 +535,14 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver {
color: backgroundColor,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.06),
color: Colors.black.withValues(alpha: 0.06),
blurRadius: 8.w,
offset: const Offset(0, -1),
),
],
border: Border(
top: BorderSide(
color: Colors.grey.withOpacity(0.15),
color: Colors.grey.withValues(alpha: 0.15),
width: 1.w,
),
),
@ -489,7 +576,8 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver {
child: Container(
padding: EdgeInsets.all(4.w),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.6),
color: Colors.black
.withValues(alpha: 0.6),
shape: BoxShape.circle,
),
child: Icon(
@ -635,7 +723,7 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver {
final isTextMode =
controller.isTextInputMode.value;
if (controller.isTextInputMode.value) {
if (isTextMode) {
// 文本输入模式 - 限制最大高度并只向上扩展
return IntroStepBuilder(
group: 'ai',
@ -686,7 +774,8 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver {
border: Border.all(
color: isDarkMode
? Colors.grey[800]!
: Colors.grey.withOpacity(0.1),
: Colors.grey
.withValues(alpha: 0.1),
width: 1.w,
),
),
@ -748,7 +837,6 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver {
),
);
} else {
Timer? _holdTimer; // 增加延迟触发的计时器
// 语音输入模式 - 按住说话模式
return Listener(
onPointerDown: (_) {
@ -775,8 +863,6 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver {
child: Obx(() {
final isPushToTalkActive =
controller.isPushToTalkActive.value;
final isListening =
controller.isListening.value;
return Container(
height: 48.w,
@ -795,8 +881,8 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver {
BorderRadius.circular(22.w),
boxShadow: [
BoxShadow(
color:
Colors.black.withOpacity(0.2),
color: Colors.black
.withValues(alpha: 0.2),
spreadRadius: 1,
blurRadius: 2,
offset: const Offset(0, 1),
@ -1127,10 +1213,10 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver {
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 8.h),
decoration: BoxDecoration(
color: primaryColor.withOpacity(0.08),
color: primaryColor.withValues(alpha: 0.08),
borderRadius: BorderRadius.circular(16.w),
border: Border.all(
color: primaryColor.withOpacity(0.2),
color: primaryColor.withValues(alpha: 0.2),
width: 1.w,
),
),

48
local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt

@ -68,8 +68,10 @@ object AgentService : CoroutineScope {
// SharedPreferences 相关常量
private const val PREF_NAME = "agent_service_prefs"
private const val KEY_IS_INTERRUPT = "is_interrupt"
private const val KEY_TTS_RATE = "tts_rate_percent"
private var _isInterrupt = AtomicBoolean(false)
private var ttsRatePercent: Int = 25
private var azureSpeechKey: String = ""
private var azureSpeechRegion: String = ""
@ -224,6 +226,7 @@ object AgentService : CoroutineScope {
// 从 SharedPreferences 读取 _isInterrupt 状态
loadIsInterruptFromCache()
loadTtsRateFromCache()
if (config["isttsinterrupt"] != null){
_isInterrupt.set(config["isttsinterrupt"]?.toString() == "true")
}
@ -401,7 +404,7 @@ object AgentService : CoroutineScope {
if (!success) {
Log.e(TAG, "Bytedance TTS服务初始化失败")
} else {
ttsService?.setSpeechParams(rate = 25, pitch = 0, volume = 100)
applyTtsSpeechParams()
val voiceName = getTtsVoiceNameByAsrCode(ttsLanguage)
if (voiceName.isNotEmpty()) {
ttsService?.setVoice(voiceName)
@ -620,6 +623,14 @@ object AgentService : CoroutineScope {
return isInterrupt
}
fun setTtsRate(rate: Int): Boolean {
val clampedRate = rate.coerceIn(-100, 100)
ttsRatePercent = clampedRate
saveTtsRateToCache(clampedRate)
applyTtsSpeechParams()
return true
}
/**
* 开始语音识别
* @param isExternalActive 是否使用外部音频源(蓝牙)
@ -1395,7 +1406,7 @@ object AgentService : CoroutineScope {
restartIdleCheck() // 状态变化,重启检测
// 直接调用TTS,无需协程包装
ttsService?.setSpeechParams(rate = 25, pitch = 0, volume = 100)
applyTtsSpeechParams()
ttsService?.speakOnce(text)
}
@ -2797,6 +2808,39 @@ object AgentService : CoroutineScope {
}
}
private fun applyTtsSpeechParams() {
try {
ttsService?.setSpeechParams(rate = ttsRatePercent, pitch = 0, volume = 100)
} catch (e: Exception) {
Log.e(TAG, "应用TTS语速参数失败: ${e.message}", e)
}
}
private fun loadTtsRateFromCache() {
try {
if (::context.isInitialized) {
val prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
val cachedValue = prefs.getInt(KEY_TTS_RATE, 25)
ttsRatePercent = cachedValue.coerceIn(-100, 100)
Log.d(TAG, "从缓存加载 ttsRatePercent: $ttsRatePercent")
}
} catch (e: Exception) {
Log.e(TAG, "加载TTS语速缓存状态时发生异常: ${e.message}", e)
}
}
private fun saveTtsRateToCache(rate: Int) {
try {
if (::context.isInitialized) {
val prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
prefs.edit().putInt(KEY_TTS_RATE, rate).apply()
Log.d(TAG, "保存 ttsRatePercent 到缓存: $rate")
}
} catch (e: Exception) {
Log.e(TAG, "保存TTS语速缓存状态时发生异常: ${e.message}", e)
}
}
/*
* 自动暂停音乐
*/

15
local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentServicePlugin.kt

@ -294,6 +294,21 @@ class AgentServicePlugin : FlutterPlugin, MethodCallHandler, EventChannel.Stream
}
}
"setTtsRate" -> {
try {
val rate = call.argument<Int>("rate")
if (rate == null) {
result.error("INVALID_ARGUMENTS", "rate参数不能为空", null)
return
}
val isSuccess = AgentService.setTtsRate(rate)
result.success(isSuccess)
} catch (e: Exception) {
Log.e(TAG, "设置TTS语速失败", e)
result.error("SET_TTS_RATE_ERROR", "设置TTS语速失败: ${e.message}", null)
}
}
"clearChatHistory" -> {
try {
AgentService.clearChatHistory { success ->

28
local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift

@ -30,6 +30,7 @@ class AgentServiceImpl: NSObject {
// UserDefaults 相关常量
private let PREF_NAME = "agent_service_prefs"
private let KEY_IS_INTERRUPT = "is_interrupt"
private let KEY_TTS_RATE = "tts_rate_percent"
private var listeners = [AgentServiceListener]()
private let listenersLock = NSLock()
@ -89,6 +90,7 @@ class AgentServiceImpl: NSObject {
// 新增:是否在识别过程中被中断
public var isInterrupt: Bool = true
private var ttsRatePercent: Int = 25
var currsessionId: String = ""
var language: String = ""
@ -208,6 +210,7 @@ class AgentServiceImpl: NSObject {
//加载 isInterrupt 状态
loadIsInterruptFromCache()
loadTtsRateFromCache()
if let isttsinterrupt = config["isttsinterrupt"] as? String {
if (isttsinterrupt == "true"){
self.isInterrupt = true
@ -343,7 +346,7 @@ class AgentServiceImpl: NSObject {
}
_ = azureTtsHelper?.setVoice(getTtsVoiceNameByAsrCode(primaryLanguage))
_ = azureTtsHelper?.setSpeechParams(rate: 25, pitch: 0, volume: 100)
_ = azureTtsHelper?.setSpeechParams(rate: ttsRatePercent, pitch: 0, volume: 100)
return true
}
@ -605,10 +608,18 @@ class AgentServiceImpl: NSObject {
return false
}
_ = azureTtsHelper?.setSpeechParams(rate: 25, pitch: 0, volume: 100)
_ = azureTtsHelper?.setSpeechParams(rate: ttsRatePercent, pitch: 0, volume: 100)
return azureTtsHelper?.speakOnce(sessionid:sessionid,text) ?? false
}
func setTtsRate(_ rate: Int) -> Bool {
let clampedRate = max(-100, min(100, rate))
ttsRatePercent = clampedRate
saveTtsRateToCache(clampedRate)
_ = azureTtsHelper?.setSpeechParams(rate: ttsRatePercent, pitch: 0, volume: 100)
return true
}
func stopTts() -> Bool {
if !isSpeaking {
return true
@ -2422,4 +2433,17 @@ extension AgentServiceImpl: AzureAsrHelper.ContinuousRecognizeCallback {
userDefaults.set(isInterrupt, forKey: KEY_IS_INTERRUPT)
os_log("保存 isInterrupt 状态到缓存: %{public}@", log: logger, type: .info, String(isInterrupt))
}
private func loadTtsRateFromCache() {
let userDefaults = UserDefaults.standard
let cachedValue = userDefaults.object(forKey: KEY_TTS_RATE) as? Int ?? 25
ttsRatePercent = max(-100, min(100, cachedValue))
os_log("从缓存加载 ttsRatePercent: %{public}@", log: logger, type: .info, String(ttsRatePercent))
}
private func saveTtsRateToCache(_ rate: Int) {
let userDefaults = UserDefaults.standard
userDefaults.set(rate, forKey: KEY_TTS_RATE)
os_log("保存 ttsRatePercent 到缓存: %{public}@", log: logger, type: .info, String(rate))
}
}

8
local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServicePlugin.swift

@ -186,6 +186,14 @@ public class AgentServicePlugin: NSObject, FlutterPlugin {
}
let success = impl.setVipExptime(vipexptime)
result(success)
case "setTtsRate":
guard let arguments = call.arguments as? [String: Any],
let rate = arguments["rate"] as? Int else {
result(FlutterError(code: "INVALID_ARGUMENTS", message: "rate参数不能为空", details: nil))
return
}
let success = impl.setTtsRate(rate)
result(success)
default:
result(FlutterMethodNotImplemented)
}

15
local_plugins/agent_service/lib/agent_service.dart

@ -1,6 +1,5 @@
import 'dart:async';
import 'dart:convert';
import 'dart:ffi';
import 'package:flutter/services.dart';
@ -249,7 +248,6 @@ class AgentService {
// 防抖相关静态字段(全局统一防抖)
// 说明:用于限制 startConversation/stopConversation 在极短时间内的重复触发
static DateTime? _lastStartConversationAt;
static DateTime? _lastStopConversationAt;
static const Duration _conversationDebounce = Duration(milliseconds: 600);
static Future<bool> startConversation({String mode = 'normal'}) async {
@ -337,7 +335,6 @@ class AgentService {
// 移除防抖拦截!
// 停止操作是安全且必要的,不应该被拦截。
// 即使频繁调用,Native 层也能处理(我们已经修复了 Native 层的并发问题)。
_lastStopConversationAt = DateTime.now();
try {
final bool result = await _channel.invokeMethod('stopConversation');
@ -406,6 +403,18 @@ class AgentService {
}
}
/// 设置TTS语速
static Future<bool> setTtsRate(int rate) async {
try {
final bool result = await _channel.invokeMethod('setTtsRate', {
'rate': rate,
});
return result;
} on PlatformException catch (e) {
throw AgentServiceException(e.code, e.message ?? '设置TTS语速失败', e.details);
}
}
/// 停止语音合成
static Future<bool> stopAiSteam() async {
try {

Loading…
Cancel
Save