diff --git a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt index a1921b405..6a72831c8 100644 --- a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt +++ b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt @@ -1113,7 +1113,7 @@ object AgentService : CoroutineScope { } /** - * 设置支持的语音识别语言 + * 设置支持的语音识别语言,同时更新TTS语言设置 */ fun setSupportedLanguages(languages: List): Boolean { if (!isInitialized) { @@ -1132,28 +1132,35 @@ object AgentService : CoroutineScope { Log.i(TAG, "设置支持的语言: ${languages.joinToString(", ")}") // 重新初始化ASR服务以应用新的语言设置 - val success = azureAsrHelper?.initialize( + val asrSuccess = azureAsrHelper?.initialize( subscriptionKey = this.azureSpeechKey, region = this.azureSpeechRegion, supportedLanguages = languages.toTypedArray(), audioSourceType = audioSourceType ) ?: false - if (success) { - Log.i(TAG, "语言设置更新成功") + // 同时更新TTS语言设置 + val ttsSuccess = updateTtsLanguage(languages.first()) // 使用第一个语言作为TTS语言 + + val overallSuccess = asrSuccess && ttsSuccess + + if (overallSuccess) { + Log.i(TAG, "ASR和TTS语言设置更新成功") sendEvent("languages_updated", mapOf( "languages" to languages, - "status" to "success" + "status" to "success", + "asr_success" to asrSuccess, + "tts_success" to ttsSuccess )) } else { - Log.e(TAG, "语言设置更新失败") + Log.e(TAG, "语言设置更新失败 - ASR: $asrSuccess, TTS: $ttsSuccess") sendEvent("error", mapOf( "code" to "LANGUAGE_UPDATE_FAILED", - "message" to "语言设置更新失败" + "message" to "语言设置更新失败 - ASR: $asrSuccess, TTS: $ttsSuccess" )) } - success + overallSuccess } catch (e: Exception) { Log.e(TAG, "设置支持语言异常: ${e.message}") sendEvent("error", mapOf( @@ -1164,6 +1171,105 @@ object AgentService : CoroutineScope { } } + /** + * 更新TTS语言设置 + * + * @param languageCode ASR语言代码,如 "zh-CN", "en-US" + * @return 是否更新成功 + */ + private fun updateTtsLanguage(languageCode: String): Boolean { + return try { + Log.i(TAG, "更新TTS语言设置: $languageCode") + + // 根据ASR语言代码映射到对应的TTS语音角色 + val voiceName = getTtsVoiceNameByAsrCode(languageCode) + + if (voiceName.isNotEmpty()) { + // 设置TTS语音角色 + val success = ttsService?.setVoice(voiceName) ?: false + + if (success) { + Log.i(TAG, "TTS语音角色设置成功: $voiceName") + } else { + Log.w(TAG, "TTS语音角色设置失败: $voiceName") + } + + success + } else { + Log.w(TAG, "未找到语言代码 $languageCode 对应的TTS语音角色") + false + } + } catch (e: Exception) { + Log.e(TAG, "更新TTS语言设置异常: ${e.message}") + false + } + } + + /** + * 根据ASR语言代码获取对应的TTS语音角色名称 + * 与LanguageManager中的映射保持一致 + * + * @param asrCode ASR语言代码,如 "zh-CN", "en-US" + * @return TTS语音角色名称 + */ + private fun getTtsVoiceNameByAsrCode(asrCode: String): String { + return when (asrCode) { + // 中文相关 + "zh-CN" -> "zh-CN-XiaoxiaoNeural" + "zh-TW" -> "zh-TW-HsiaoChenNeural" + "zh-HK" -> "zh-HK-HiuMaanNeural" + + // 英语相关 + "en-US" -> "en-US-AriaNeural" + + // 亚洲语言 + "ja-JP" -> "ja-JP-NanamiNeural" + "ko-KR" -> "ko-KR-SunHiNeural" + "th-TH" -> "th-TH-PremwadeeNeural" + "vi-VN" -> "vi-VN-HoaiMyNeural" + "id-ID" -> "id-ID-GadisNeural" + "hi-IN" -> "hi-IN-SwaraNeural" + "ms-MY" -> "ms-MY-YasminNeural" + "bn-IN" -> "bn-IN-TanishaaNeural" + "ta-IN" -> "ta-IN-PallaviNeural" + "te-IN" -> "te-IN-ShrutiNeural" + "mr-IN" -> "mr-IN-AarohiNeural" + "ur-IN" -> "ur-IN-GulNeural" + "tr-TR" -> "tr-TR-EmelNeural" + "fa-IR" -> "fa-IR-DilaraNeural" + + // 欧洲语言 + "fr-FR" -> "fr-FR-DeniseNeural" + "es-ES" -> "es-ES-ElviraNeural" + "pt-BR" -> "pt-BR-FranciscaNeural" + "it-IT" -> "it-IT-ElsaNeural" + "de-DE" -> "de-DE-KatjaNeural" + "ru-RU" -> "ru-RU-SvetlanaNeural" + "pl-PL" -> "pl-PL-AgnieszkaNeural" + "nl-NL" -> "nl-NL-ColetteNeural" + "sv-SE" -> "sv-SE-SofieNeural" + "cs-CZ" -> "cs-CZ-VlastaNeural" + "el-GR" -> "el-GR-AthinaNeural" + "ro-RO" -> "ro-RO-AlinaNeural" + "hu-HU" -> "hu-HU-NoemiNeural" + "uk-UA" -> "uk-UA-PolinaNeural" + "da-DK" -> "da-DK-ChristelNeural" + "fi-FI" -> "fi-FI-NooraNeural" + "nb-NO" -> "nb-NO-IselinNeural" + "hr-HR" -> "hr-HR-GabrijelaNeural" + "ca-ES" -> "ca-ES-JoanaNeural" + + // 阿拉伯语和非洲语言 + "ar-EG" -> "ar-EG-SalmaNeural" + "sw-KE" -> "sw-KE-ZuriNeural" + + else -> { + Log.w(TAG, "未支持的语言代码: $asrCode,使用默认英语语音") + "en-US-AriaNeural" + } + } + } + /** * 保存聊天消息 */ diff --git a/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift b/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift index b884a0c71..5362f880f 100644 --- a/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift +++ b/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift @@ -879,53 +879,194 @@ private func jsonToString(_ json: [String: Any]) -> String? { } /** - * 设置支持的语音识别语言 - * + * 设置支持的语音识别语言,同时更新TTS语言设置 + * * @param languages 支持的语言列表,如 ["zh-CN", "en-US"] * @return 是否设置成功 */ func setSupportedLanguages(_ languages: [String]) -> Bool { os_log("设置支持的语言: %{public}@", log: logger, type: .info, languages.joined(separator: ", ")) - + if !isInitialized { os_log("服务未初始化,无法设置语言", log: logger, type: .error) sendError("服务未初始化", code: "NOT_INITIALIZED") return false } - + if languages.isEmpty { os_log("语言列表为空,使用默认语言", log: logger, type: .info) return false } - + // 如果正在识别,需要先停止 let wasRecognizing = isRecognizing if wasRecognizing { _ = stopRecognition() } - + // 重新初始化ASR引擎,使用新的语言设置 - guard let success = azureAsrHelper?.initialize( + guard let asrSuccess = azureAsrHelper?.initialize( subscriptionKey: azureSpeechKey, region: azureSpeechRegion, supportedLanguages: languages, audioSourceType: .microphone - ), success else { + ), asrSuccess else { os_log("重新初始化ASR引擎失败", log: logger, type: .error) sendError("重新初始化ASR引擎失败", code: "ASR_INIT_ERROR") return false } - - os_log("成功设置支持的语言: %{public}@", log: logger, type: .info, languages.joined(separator: ", ")) - + + // 同时更新TTS语言设置 + let ttsSuccess = updateTtsLanguage(languages.first ?? "zh-CN") + + let overallSuccess = asrSuccess && ttsSuccess + + if overallSuccess { + os_log("ASR和TTS语言设置更新成功: %{public}@", log: logger, type: .info, languages.joined(separator: ", ")) + } else { + os_log("语言设置更新失败 - ASR: %{public}@, TTS: %{public}@", log: logger, type: .error, String(asrSuccess), String(ttsSuccess)) + } + // 如果之前正在识别,重新开始 if wasRecognizing { _ = startRecognition() } - - return true + + return overallSuccess } - + + /** + * 更新TTS语言设置 + * + * @param languageCode ASR语言代码,如 "zh-CN", "en-US" + * @return 是否更新成功 + */ + private func updateTtsLanguage(_ languageCode: String) -> Bool { + os_log("更新TTS语言设置: %{public}@", log: logger, type: .info, languageCode) + + // 根据ASR语言代码映射到对应的TTS语音角色 + let voiceName = getTtsVoiceNameByAsrCode(languageCode) + + if !voiceName.isEmpty { + // 设置TTS语音角色 + let success = azureTtsHelper?.setVoice(voiceName) ?? false + + if success { + os_log("TTS语音角色设置成功: %{public}@", log: logger, type: .info, voiceName) + } else { + os_log("TTS语音角色设置失败: %{public}@", log: logger, type: .error, voiceName) + } + + return success + } else { + os_log("未找到语言代码 %{public}@ 对应的TTS语音角色", log: logger, type: .error, languageCode) + return false + } + } + + /** + * 根据ASR语言代码获取对应的TTS语音角色名称 + * 与LanguageManager中的映射保持一致 + * + * @param asrCode ASR语言代码,如 "zh-CN", "en-US" + * @return TTS语音角色名称 + */ + private func getTtsVoiceNameByAsrCode(_ asrCode: String) -> String { + switch asrCode { + // 中文相关 + case "zh-CN": + return "zh-CN-XiaoxiaoNeural" + case "zh-TW": + return "zh-TW-HsiaoChenNeural" + case "zh-HK": + return "zh-HK-HiuMaanNeural" + + // 英语相关 + case "en-US": + return "en-US-AriaNeural" + + // 亚洲语言 + case "ja-JP": + return "ja-JP-NanamiNeural" + case "ko-KR": + return "ko-KR-SunHiNeural" + case "th-TH": + return "th-TH-PremwadeeNeural" + case "vi-VN": + return "vi-VN-HoaiMyNeural" + case "id-ID": + return "id-ID-GadisNeural" + case "hi-IN": + return "hi-IN-SwaraNeural" + case "ms-MY": + return "ms-MY-YasminNeural" + case "bn-IN": + return "bn-IN-TanishaaNeural" + case "ta-IN": + return "ta-IN-PallaviNeural" + case "te-IN": + return "te-IN-ShrutiNeural" + case "mr-IN": + return "mr-IN-AarohiNeural" + case "ur-IN": + return "ur-IN-GulNeural" + case "tr-TR": + return "tr-TR-EmelNeural" + case "fa-IR": + return "fa-IR-DilaraNeural" + + // 欧洲语言 + case "fr-FR": + return "fr-FR-DeniseNeural" + case "es-ES": + return "es-ES-ElviraNeural" + case "pt-BR": + return "pt-BR-FranciscaNeural" + case "it-IT": + return "it-IT-ElsaNeural" + case "de-DE": + return "de-DE-KatjaNeural" + case "ru-RU": + return "ru-RU-SvetlanaNeural" + case "pl-PL": + return "pl-PL-AgnieszkaNeural" + case "nl-NL": + return "nl-NL-ColetteNeural" + case "sv-SE": + return "sv-SE-SofieNeural" + case "cs-CZ": + return "cs-CZ-VlastaNeural" + case "el-GR": + return "el-GR-AthinaNeural" + case "ro-RO": + return "ro-RO-AlinaNeural" + case "hu-HU": + return "hu-HU-NoemiNeural" + case "uk-UA": + return "uk-UA-PolinaNeural" + case "da-DK": + return "da-DK-ChristelNeural" + case "fi-FI": + return "fi-FI-NooraNeural" + case "nb-NO": + return "nb-NO-IselinNeural" + case "hr-HR": + return "hr-HR-GabrijelaNeural" + case "ca-ES": + return "ca-ES-JoanaNeural" + + // 阿拉伯语和非洲语言 + case "ar-EG": + return "ar-EG-SalmaNeural" + case "sw-KE": + return "sw-KE-ZuriNeural" + + default: + os_log("未支持的语言代码: %{public}@,使用默认英语语音", log: logger, type: .error, asrCode) + return "en-US-AriaNeural" + } + } + func dispose() -> Bool { print("ai释放资源") if isRecognizing {