Browse Source

修复ios通话奔溃问题,语言不播报问题

weicu
fdp 1 year ago
parent
commit
fab2286cdf
  1. 152
      lib/data/services/language_manager.dart
  2. 11
      local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrToAsr.kt
  3. 33
      local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/IntegratedSpeechTranslationService.swift
  4. 56
      local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/VolcanoTranslationServiceImpl.swift

152
lib/data/services/language_manager.dart

@ -5,22 +5,22 @@ import '../../core/utils/logger.dart';
class LanguageInfo {
/// 短代码,如 'zh', 'en'
final String shortCode;
/// 中文名称
final String chineseName;
/// 英文名称
final String englishName;
/// Azure ASR 语言代码,如 'zh-CN', 'en-US'
final String asrCode;
/// Azure TTS 语音名称
final String ttsVoiceName;
/// 备注信息
final String? note;
LanguageInfo({
required this.shortCode,
required this.chineseName,
@ -35,30 +35,30 @@ class LanguageInfo {
class LanguageManager extends GetxService {
// 单例实例
static LanguageManager? _instance;
// 单例工厂方法
factory LanguageManager() {
_instance ??= LanguageManager._internal();
return _instance!;
}
// 私有构造函数
LanguageManager._internal() {
_initLanguageMap();
}
// 语言信息映射表,以 ASR 代码为键
final Map<String, LanguageInfo> _languageMap = {};
// 中文名称到 ASR 代码的映射
final Map<String, String> _chineseNameToAsrCodeMap = {};
// 英文名称到 ASR 代码的映射
final Map<String, String> _englishNameToAsrCodeMap = {};
// 短代码到 ASR 代码的映射
final Map<String, String> _shortCodeToAsrCodeMap = {};
/// 初始化语言映射表
void _initLanguageMap() {
// 中文相关
@ -69,7 +69,7 @@ class LanguageManager extends GetxService {
asrCode: 'zh-CN',
ttsVoiceName: 'zh-CN-XiaoxiaoNeural',
);
_addLanguage(
shortCode: 'zh-Hant',
chineseName: '中文(繁体)',
@ -77,7 +77,7 @@ class LanguageManager extends GetxService {
asrCode: 'zh-TW',
ttsVoiceName: 'zh-TW-HsiaoChenNeural',
);
_addLanguage(
shortCode: 'zh-Hant-hk',
chineseName: '中文(香港繁体)',
@ -85,7 +85,7 @@ class LanguageManager extends GetxService {
asrCode: 'zh-HK',
ttsVoiceName: 'zh-HK-HiuMaanNeural',
);
// 英语相关
_addLanguage(
shortCode: 'en',
@ -94,7 +94,7 @@ class LanguageManager extends GetxService {
asrCode: 'en-US',
ttsVoiceName: 'en-US-AriaNeural',
);
// 日语相关
_addLanguage(
shortCode: 'ja',
@ -103,7 +103,7 @@ class LanguageManager extends GetxService {
asrCode: 'ja-JP',
ttsVoiceName: 'ja-JP-NanamiNeural',
);
// 韩语相关
_addLanguage(
shortCode: 'ko',
@ -112,7 +112,7 @@ class LanguageManager extends GetxService {
asrCode: 'ko-KR',
ttsVoiceName: 'ko-KR-SunHiNeural',
);
// 欧洲语言
_addLanguage(
shortCode: 'fr',
@ -121,7 +121,7 @@ class LanguageManager extends GetxService {
asrCode: 'fr-FR',
ttsVoiceName: 'fr-FR-DeniseNeural',
);
_addLanguage(
shortCode: 'es',
chineseName: '西班牙语',
@ -129,7 +129,7 @@ class LanguageManager extends GetxService {
asrCode: 'es-ES',
ttsVoiceName: 'es-ES-ElviraNeural',
);
_addLanguage(
shortCode: 'pt',
chineseName: '葡萄牙语',
@ -137,7 +137,7 @@ class LanguageManager extends GetxService {
asrCode: 'pt-BR',
ttsVoiceName: 'pt-BR-FranciscaNeural',
);
_addLanguage(
shortCode: 'it',
chineseName: '意大利语',
@ -145,7 +145,7 @@ class LanguageManager extends GetxService {
asrCode: 'it-IT',
ttsVoiceName: 'it-IT-ElsaNeural',
);
_addLanguage(
shortCode: 'de',
chineseName: '德语',
@ -153,7 +153,7 @@ class LanguageManager extends GetxService {
asrCode: 'de-DE',
ttsVoiceName: 'de-DE-KatjaNeural',
);
_addLanguage(
shortCode: 'ru',
chineseName: '俄语',
@ -161,7 +161,7 @@ class LanguageManager extends GetxService {
asrCode: 'ru-RU',
ttsVoiceName: 'ru-RU-SvetlanaNeural',
);
_addLanguage(
shortCode: 'pl',
chineseName: '波兰语',
@ -169,7 +169,7 @@ class LanguageManager extends GetxService {
asrCode: 'pl-PL',
ttsVoiceName: 'pl-PL-AgnieszkaNeural',
);
_addLanguage(
shortCode: 'nl',
chineseName: '荷兰语',
@ -177,7 +177,7 @@ class LanguageManager extends GetxService {
asrCode: 'nl-NL',
ttsVoiceName: 'nl-NL-ColetteNeural',
);
_addLanguage(
shortCode: 'sv',
chineseName: '瑞典语',
@ -185,7 +185,7 @@ class LanguageManager extends GetxService {
asrCode: 'sv-SE',
ttsVoiceName: 'sv-SE-SofieNeural',
);
_addLanguage(
shortCode: 'cs',
chineseName: '捷克语',
@ -193,7 +193,7 @@ class LanguageManager extends GetxService {
asrCode: 'cs-CZ',
ttsVoiceName: 'cs-CZ-VlastaNeural',
);
_addLanguage(
shortCode: 'el',
chineseName: '现代希腊语',
@ -201,7 +201,7 @@ class LanguageManager extends GetxService {
asrCode: 'el-GR',
ttsVoiceName: 'el-GR-AthinaNeural',
);
_addLanguage(
shortCode: 'ro',
chineseName: '罗马尼亚语',
@ -209,7 +209,7 @@ class LanguageManager extends GetxService {
asrCode: 'ro-RO',
ttsVoiceName: 'ro-RO-AlinaNeural',
);
_addLanguage(
shortCode: 'hu',
chineseName: '匈牙利语',
@ -217,7 +217,7 @@ class LanguageManager extends GetxService {
asrCode: 'hu-HU',
ttsVoiceName: 'hu-HU-NoemiNeural',
);
_addLanguage(
shortCode: 'uk',
chineseName: '乌克兰语',
@ -225,7 +225,7 @@ class LanguageManager extends GetxService {
asrCode: 'uk-UA',
ttsVoiceName: 'uk-UA-PolinaNeural',
);
_addLanguage(
shortCode: 'da',
chineseName: '丹麦语',
@ -233,7 +233,7 @@ class LanguageManager extends GetxService {
asrCode: 'da-DK',
ttsVoiceName: 'da-DK-ChristelNeural',
);
_addLanguage(
shortCode: 'fi',
chineseName: '芬兰语',
@ -241,7 +241,7 @@ class LanguageManager extends GetxService {
asrCode: 'fi-FI',
ttsVoiceName: 'fi-FI-NooraNeural',
);
_addLanguage(
shortCode: 'no',
chineseName: '挪威语',
@ -249,7 +249,7 @@ class LanguageManager extends GetxService {
asrCode: 'nb-NO',
ttsVoiceName: 'nb-NO-IselinNeural',
);
_addLanguage(
shortCode: 'hr',
chineseName: '克罗地亚语',
@ -257,7 +257,7 @@ class LanguageManager extends GetxService {
asrCode: 'hr-HR',
ttsVoiceName: 'hr-HR-GabrijelaNeural',
);
_addLanguage(
shortCode: 'ca',
chineseName: '加泰隆语',
@ -265,7 +265,7 @@ class LanguageManager extends GetxService {
asrCode: 'ca-ES',
ttsVoiceName: 'ca-ES-JoanaNeural',
);
// 亚洲语言
_addLanguage(
shortCode: 'ar',
@ -274,7 +274,7 @@ class LanguageManager extends GetxService {
asrCode: 'ar-EG',
ttsVoiceName: 'ar-EG-SalmaNeural',
);
_addLanguage(
shortCode: 'th',
chineseName: '泰语',
@ -282,7 +282,7 @@ class LanguageManager extends GetxService {
asrCode: 'th-TH',
ttsVoiceName: 'th-TH-PremwadeeNeural',
);
_addLanguage(
shortCode: 'vi',
chineseName: '越南语',
@ -290,7 +290,7 @@ class LanguageManager extends GetxService {
asrCode: 'vi-VN',
ttsVoiceName: 'vi-VN-HoaiMyNeural',
);
_addLanguage(
shortCode: 'id',
chineseName: '印尼语',
@ -298,7 +298,7 @@ class LanguageManager extends GetxService {
asrCode: 'id-ID',
ttsVoiceName: 'id-ID-GadisNeural',
);
_addLanguage(
shortCode: 'hi',
chineseName: '印地语',
@ -306,7 +306,7 @@ class LanguageManager extends GetxService {
asrCode: 'hi-IN',
ttsVoiceName: 'hi-IN-SwaraNeural',
);
_addLanguage(
shortCode: 'tr',
chineseName: '土耳其语',
@ -314,7 +314,7 @@ class LanguageManager extends GetxService {
asrCode: 'tr-TR',
ttsVoiceName: 'tr-TR-EmelNeural',
);
_addLanguage(
shortCode: 'ms',
chineseName: '马来语',
@ -322,7 +322,7 @@ class LanguageManager extends GetxService {
asrCode: 'ms-MY',
ttsVoiceName: 'ms-MY-YasminNeural',
);
_addLanguage(
shortCode: 'bn',
chineseName: '孟加拉语',
@ -330,7 +330,7 @@ class LanguageManager extends GetxService {
asrCode: 'bn-IN',
ttsVoiceName: 'bn-IN-TanishaaNeural',
);
_addLanguage(
shortCode: 'ta',
chineseName: '泰米尔语',
@ -338,7 +338,7 @@ class LanguageManager extends GetxService {
asrCode: 'ta-IN',
ttsVoiceName: 'ta-IN-PallaviNeural',
);
_addLanguage(
shortCode: 'te',
chineseName: '泰卢固语',
@ -346,7 +346,7 @@ class LanguageManager extends GetxService {
asrCode: 'te-IN',
ttsVoiceName: 'te-IN-ShrutiNeural',
);
_addLanguage(
shortCode: 'mr',
chineseName: '马拉提语',
@ -354,7 +354,7 @@ class LanguageManager extends GetxService {
asrCode: 'mr-IN',
ttsVoiceName: 'mr-IN-AarohiNeural',
);
_addLanguage(
shortCode: 'ur',
chineseName: '乌尔都语',
@ -362,7 +362,7 @@ class LanguageManager extends GetxService {
asrCode: 'ur-IN',
ttsVoiceName: 'ur-IN-GulNeural',
);
_addLanguage(
shortCode: 'fa',
chineseName: '波斯语',
@ -370,7 +370,7 @@ class LanguageManager extends GetxService {
asrCode: 'fa-IR',
ttsVoiceName: 'fa-IR-DilaraNeural',
);
// 非洲语言
_addLanguage(
shortCode: 'sw',
@ -379,10 +379,10 @@ class LanguageManager extends GetxService {
asrCode: 'sw-KE',
ttsVoiceName: 'sw-KE-ZuriNeural',
);
Logger.info('语言管理器初始化完成,共加载 ${_languageMap.length} 种语言');
}
/// 添加语言信息到映射表
void _addLanguage({
required String shortCode,
@ -400,133 +400,133 @@ class LanguageManager extends GetxService {
ttsVoiceName: ttsVoiceName,
note: note,
);
_languageMap[asrCode] = languageInfo;
_chineseNameToAsrCodeMap[chineseName] = asrCode;
_englishNameToAsrCodeMap[englishName] = asrCode;
_shortCodeToAsrCodeMap[shortCode] = asrCode;
}
/// 获取所有支持的语言信息
Map<String, LanguageInfo> getAllLanguages() {
return Map.from(_languageMap);
}
/// 获取所有支持的语言的中文名称
List<String> getAllChineseNames() {
return _chineseNameToAsrCodeMap.keys.toList();
}
/// 获取所有支持的语言的英文名称
List<String> getAllEnglishNames() {
return _englishNameToAsrCodeMap.keys.toList();
}
/// 根据 ASR 代码获取语言信息
LanguageInfo? getLanguageByAsrCode(String asrCode) {
return _languageMap[asrCode];
}
/// 根据中文名称获取语言信息
LanguageInfo? getLanguageByChineseName(String chineseName) {
final asrCode = _chineseNameToAsrCodeMap[chineseName];
if (asrCode == null) return null;
return _languageMap[asrCode];
}
/// 根据英文名称获取语言信息
LanguageInfo? getLanguageByEnglishName(String englishName) {
final asrCode = _englishNameToAsrCodeMap[englishName];
if (asrCode == null) return null;
return _languageMap[asrCode];
}
/// 根据短代码获取语言信息
LanguageInfo? getLanguageByShortCode(String shortCode) {
final asrCode = _shortCodeToAsrCodeMap[shortCode];
if (asrCode == null) return null;
return _languageMap[asrCode];
}
/// 根据中文名称获取 ASR 代码
String? getAsrCodeByChineseName(String chineseName) {
return _chineseNameToAsrCodeMap[chineseName];
}
/// 根据英文名称获取 ASR 代码
String? getAsrCodeByEnglishName(String englishName) {
return _englishNameToAsrCodeMap[englishName];
}
/// 根据短代码获取 ASR 代码
String? getAsrCodeByShortCode(String shortCode) {
return _shortCodeToAsrCodeMap[shortCode];
}
/// 根据 ASR 代码获取中文名称
String? getChineseNameByAsrCode(String asrCode) {
final languageInfo = _languageMap[asrCode];
return languageInfo?.chineseName;
}
/// 根据 ASR 代码获取英文名称
String? getEnglishNameByAsrCode(String asrCode) {
final languageInfo = _languageMap[asrCode];
return languageInfo?.englishName;
}
/// 根据 ASR 代码获取短代码
String? getShortCodeByAsrCode(String asrCode) {
final languageInfo = _languageMap[asrCode];
return languageInfo?.shortCode;
}
/// 根据 ASR 代码获取 TTS 语音名称
String? getTtsVoiceNameByAsrCode(String asrCode) {
final languageInfo = _languageMap[asrCode];
return languageInfo?.ttsVoiceName;
}
/// 根据中文名称获取 TTS 语音名称
String? getTtsVoiceNameByChineseName(String chineseName) {
final asrCode = _chineseNameToAsrCodeMap[chineseName];
if (asrCode == null) return null;
return _languageMap[asrCode]?.ttsVoiceName;
}
/// 根据短代码获取中文名称
String? getChineseNameByShortCode(String shortCode) {
final asrCode = _shortCodeToAsrCodeMap[shortCode];
if (asrCode == null) return null;
return _languageMap[asrCode]?.chineseName;
}
/// 根据短代码获取英文名称
String? getEnglishNameByShortCode(String shortCode) {
final asrCode = _shortCodeToAsrCodeMap[shortCode];
if (asrCode == null) return null;
return _languageMap[asrCode]?.englishName;
}
/// 根据短代码获取 TTS 语音名称
String? getTtsVoiceNameByShortCode(String shortCode) {
final asrCode = _shortCodeToAsrCodeMap[shortCode];
if (asrCode == null) return null;
return _languageMap[asrCode]?.ttsVoiceName;
}
/// 获取中文名称到 ASR 代码的映射
Map<String, String> getChineseNameToAsrCodeMap() {
return Map.from(_chineseNameToAsrCodeMap);
}
/// 获取英文名称到 ASR 代码的映射
Map<String, String> getEnglishNameToAsrCodeMap() {
return Map.from(_englishNameToAsrCodeMap);
}
/// 获取短代码到 ASR 代码的映射
Map<String, String> getShortCodeToAsrCodeMap() {
return Map.from(_shortCodeToAsrCodeMap);
}
}
}

11
local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrToAsr.kt

@ -1315,7 +1315,16 @@ class VolcanoTranslationServiceImpl :
"ms-MY" to "ms", // 马来语(马来西亚)
"id-ID" to "id", // 印尼语(印尼)
"fil-PH" to "tl", // 菲律宾语(菲律宾)
"sv-SE" to "sv", // 瑞典语(瑞典)
"ro-RO" to "ro", // 罗马尼亚语(罗马尼亚)
"hu-HU" to "hu", // 匈牙利语(匈牙利)
"da-DK" to "da", // 丹麦语(丹麦)
"fi-FI" to "fi", // 芬兰语(芬兰)
"nb-NO" to "nb", // 挪威语(挪威)
"hr-HR" to "hr", // 克罗地亚语(克罗地亚)
"ca-ES" to "ca", // 加泰隆语
"mr-IN" to "mr", // 马拉地语(印度)
"ur-IN" to "ur", // 乌尔都语(印度)
// 欧洲
"en-US" to "en", // 英语(美国)
"en-GB" to "en", // 英语(英国)

33
local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/IntegratedSpeechTranslationService.swift

@ -605,13 +605,36 @@ import os.log
audioProcessor?.stopMicrophoneCapture()
recordFile?.closeFile(isSave: true)
// 等待当前处理完成
while serviceState.isTranslating || serviceState.isSynthesizing {
Thread.sleep(forTimeInterval: 0.1)
// 使用后台队列等待当前处理完成,避免阻塞主线程
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
guard let self = self else { return }
// 设置超时时间,避免无限等待
let timeout = DispatchTime.now() + .seconds(5)
while (self.serviceState.isTranslating || self.serviceState.isSynthesizing) && DispatchTime.now() < timeout {
Thread.sleep(forTimeInterval: 0.1)
}
// 强制停止合成器(如果仍在运行)
if self.serviceState.isSynthesizing {
do {
try self.synthesizer?.stopSpeaking()
} catch {
os_log("停止语音合成失败: %@", log: self.log, type: .error, error.localizedDescription)
}
self.serviceState.isSynthesizing = false
DispatchQueue.main.async {
self.eventCallback?.onStateChanged(component: "Synthesis", isActive: false)
}
}
DispatchQueue.main.async {
os_log("停止连续语音翻译", log: self.log, type: .info)
}
}
os_log("停止连续语音翻译", log: log, type: .info)
} catch {
os_log("停止翻译失败: %@", log: log, type: .error, error.localizedDescription)
eventCallback?.onError(component: "Service", error: "停止失败: \(error.localizedDescription)")

56
local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/VolcanoTranslationServiceImpl.swift

@ -24,6 +24,8 @@ public class VolcanoTranslationServiceImpl: IntegratedSpeechTranslationService.T
private var region = "cn-north-1"
private var maxRetryAttempts = 3
private var timeout: TimeInterval = 10.0
private var isDisposed = false // 添加释放状态标志
private let disposeLock = NSLock() // 添加线程安全锁
// HTTP客户端
private lazy var urlSession: URLSession = {
@ -52,7 +54,16 @@ public class VolcanoTranslationServiceImpl: IntegratedSpeechTranslationService.T
"ms-MY": "ms", // 马来语(马来西亚)
"id-ID": "id", // 印尼语(印尼)
"fil-PH": "tl", // 菲律宾语(菲律宾)
"sv-SE": "sv", // 瑞典语(瑞典)
"ro-RO": "ro", // 罗马尼亚语(罗马尼亚)
"hu-HU": "hu", // 匈牙利语(匈牙利)
"da-DK": "da", // 丹麦语(丹麦)
"fi-FI": "fi", // 芬兰语(芬兰)
"nb-NO": "nb", // 挪威语(挪威)
"hr-HR": "hr", // 克罗地亚语(克罗地亚)
"ca-ES": "ca", // 加泰隆语
"mr-IN": "mr", // 马拉地语(印度)
"ur-IN": "ur", // 乌尔都语(印度)
// 欧洲
"en-US": "en", // 英语(美国)
"en-GB": "en", // 英语(英国)
@ -150,11 +161,25 @@ public class VolcanoTranslationServiceImpl: IntegratedSpeechTranslationService.T
/**
* 释放资源
* 添加线程安全保护,防止重复释放
*/
public func dispose() {
disposeLock.lock()
defer { disposeLock.unlock() }
if isDisposed {
os_log("翻译服务已经释放,跳过重复释放", log: log, type: .info)
return
}
os_log("开始释放翻译服务资源", log: log, type: .info)
isDisposed = true
isInitialized = false
// 取消并失效URLSession
urlSession.invalidateAndCancel()
os_log("火山翻译服务已释放", log: log, type: .info)
os_log("翻译服务资源释放完成", log: log, type: .info)
}
// MARK: - Private Methods
@ -203,6 +228,7 @@ public class VolcanoTranslationServiceImpl: IntegratedSpeechTranslationService.T
/**
* 执行翻译
* 添加服务状态检查,防止在已释放的会话中创建任务
*/
private func performTranslation(
text: String,
@ -210,6 +236,19 @@ public class VolcanoTranslationServiceImpl: IntegratedSpeechTranslationService.T
targetLanguage: String
) async -> IntegratedSpeechTranslationService.TranslationResult {
// 检查服务是否已被释放
disposeLock.lock()
let disposed = isDisposed
disposeLock.unlock()
if disposed {
os_log("翻译服务已释放,取消翻译请求", log: log, type: .error)
return IntegratedSpeechTranslationService.TranslationResult(
success: false,
error: "翻译服务已释放"
)
}
do {
// 转换语言代码
let sourceCode = getLanguageCode(languageCode: sourceLanguage)
@ -263,6 +302,19 @@ public class VolcanoTranslationServiceImpl: IntegratedSpeechTranslationService.T
os_log("发送翻译请求到: %@", log: log, type: .debug, url.absoluteString)
// 再次检查服务状态,防止在请求构建过程中被释放
disposeLock.lock()
let stillDisposed = isDisposed
disposeLock.unlock()
if stillDisposed {
os_log("翻译服务在请求过程中被释放,取消网络请求", log: log, type: .error)
return IntegratedSpeechTranslationService.TranslationResult(
success: false,
error: "翻译服务已释放"
)
}
// 发送请求
let (data, response) = try await urlSession.data(for: request)

Loading…
Cancel
Save