Browse Source

Merge branch 'new_dev' of https://github.com/deepcloud2048/deep_voice into new_dev

newdev_shunjiawei
liwei1dao 1 year ago
parent
commit
c7fe767c9a
  1. 20
      lib/modules/agent/controllers/agent_controller.dart
  2. 9
      lib/modules/translation/controllers/translation_controller.dart
  3. 132
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt
  4. 189
      local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift
  5. 9
      local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServicePlugin.swift
  6. 29
      local_plugins/agent_service/lib/agent_service.dart
  7. 2
      local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt
  8. 36
      local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift
  9. 15
      local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureSpeechPlugin.swift

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

@ -3,6 +3,8 @@ import 'dart:convert';
import 'dart:ffi'; import 'dart:ffi';
import 'dart:io'; import 'dart:io';
import 'package:amap_service/amap_service.dart'; import 'package:amap_service/amap_service.dart';
import '../../../data/services/ble_manager.dart';
import '../../../data/services/bluetooth_manager.dart';
import '../../../data/services/novice_guide_service.dart'; import '../../../data/services/novice_guide_service.dart';
import '/core/utils/upload_oss.dart'; import '/core/utils/upload_oss.dart';
import '/data/models/user_Info.dart'; import '/data/models/user_Info.dart';
@ -41,7 +43,10 @@ class AgentController extends GetxController {
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
final noviceGuideService = Get.find<NoviceGuideService>(); final noviceGuideService = Get.find<NoviceGuideService>();
// 确保服务已初始化
// ========== 依赖服务 ==========
final BluetoothManager _bluetoothManager = Get.find<BluetoothManager>();
final BleManager _bleManager = Get.find<BleManager>();
//音乐播放管理 //音乐播放管理
final _musiceManager = Get.find<MusicManager>(); final _musiceManager = Get.find<MusicManager>();
@ -558,16 +563,16 @@ class AgentController extends GetxController {
break; break;
case AgentServiceEventType.ttsStarted: case AgentServiceEventType.ttsStarted:
// isSpeaking.value = true; isSpeaking.value = true;
// Logger.i(TAG, 'TTS开始播放'); print('TTS开始播放');
break; break;
case AgentServiceEventType.ttsCompleted: case AgentServiceEventType.ttsCompleted:
case AgentServiceEventType.ttsStopped: case AgentServiceEventType.ttsStopped:
case AgentServiceEventType.ttsCanceled: case AgentServiceEventType.ttsCanceled:
// isSpeaking.value = false; isSpeaking.value = false;
// Logger.i(TAG, 'TTS停止播放, $event.type'); Logger.i(TAG, 'TTS停止播放, $event.type');
break; break;
case AgentServiceEventType.playbackStarted: case AgentServiceEventType.playbackStarted:
@ -1035,6 +1040,11 @@ class AgentController extends GetxController {
try { try {
await AgentService.recognizeCallback(); await AgentService.recognizeCallback();
if (_bluetoothManager.currentDeviceRx.value != null) {
await AgentService.restoreOriginalAudioState();
} else {
await AgentService.disableBluetoothAudio();
}
} catch (e) { } catch (e) {
Logger.e(TAG, '启动识别器失败: $e'); Logger.e(TAG, '启动识别器失败: $e');
} }

9
lib/modules/translation/controllers/translation_controller.dart

@ -9,6 +9,7 @@ import 'package:get_storage/get_storage.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import '../../../data/services/bluetooth_manager.dart';
import '../../../data/services/music_manager.dart'; import '../../../data/services/music_manager.dart';
import '../../../data/services/volcano_translation_service.dart'; import '../../../data/services/volcano_translation_service.dart';
import '../../../data/services/tts_service.dart'; import '../../../data/services/tts_service.dart';
@ -30,6 +31,8 @@ class TranslationController extends GetxController {
static const String TAG = 'TranslationController'; static const String TAG = 'TranslationController';
// 服务 // 服务
final BluetoothManager _bluetoothManager = Get.find<BluetoothManager>();
final BleManager _bleManager = Get.find<BleManager>();
final AsrService _asrService = Get.find<AsrService>(); final AsrService _asrService = Get.find<AsrService>();
final VolcanoTranslationService _translationService = final VolcanoTranslationService _translationService =
Get.find<VolcanoTranslationService>(); Get.find<VolcanoTranslationService>();
@ -483,7 +486,11 @@ class TranslationController extends GetxController {
if (currentMode.value == 'faceToFace') { if (currentMode.value == 'faceToFace') {
_asrService.stopContinuousRecognition(); _asrService.stopContinuousRecognition();
} else { } else {
await _ttsService.setAudioOutputDevice(0); if (_bluetoothManager.currentDeviceRx.value != null) {
restoreOriginalAudioState();
} else {
disableBluetoothAudio();
}
await _asrService.startContinuousRecognition(_audioSourceType); await _asrService.startContinuousRecognition(_audioSourceType);
// 开始ASR活跃时长计时 // 开始ASR活跃时长计时
_startAsrActiveTracking(); _startAsrActiveTracking();

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

@ -420,7 +420,7 @@ object AgentService : CoroutineScope {
try { try {
delay(maxIdleSeconds * 1000L) delay(maxIdleSeconds * 1000L)
if (isActive && isRecognitionActive && !hasSpeechDetected && !isTtsSpeaking && !isAiStreaming) { if (isActive && isRecognitionActive && !isTtsSpeaking && !isAiStreaming) {
stopRecognition() stopRecognition()
sendEvent( sendEvent(
"auto_stop", "auto_stop",
@ -522,12 +522,12 @@ object AgentService : CoroutineScope {
override fun onRecognizing(recognizing: String, detectedLanguage: String) { override fun onRecognizing(recognizing: String, detectedLanguage: String) {
if (recognizing.isNotEmpty()) { if (recognizing.isNotEmpty()) {
// 检测到语音,更新状态 // 检测到语音,更新状态
val previousHasSpeech = hasSpeechDetected val previousHasSpeech = hasSpeechDetected
_hasSpeechDetected.set(true) _hasSpeechDetected.set(true)
if (!previousHasSpeech) { if (!previousHasSpeech) {
restartIdleCheck() restartIdleCheck()
} }
sendEvent( sendEvent(
"recognizing", mapOf( "recognizing", mapOf(
@ -1115,7 +1115,7 @@ object AgentService : CoroutineScope {
} }
/** /**
* 设置支持的语音识别语言 * 设置支持的语音识别语言,同时更新TTS语言设置
*/ */
fun setSupportedLanguages(languages: List<String>): Boolean { fun setSupportedLanguages(languages: List<String>): Boolean {
if (!isInitialized) { if (!isInitialized) {
@ -1134,28 +1134,35 @@ object AgentService : CoroutineScope {
Log.i(TAG, "设置支持的语言: ${languages.joinToString(", ")}") Log.i(TAG, "设置支持的语言: ${languages.joinToString(", ")}")
// 重新初始化ASR服务以应用新的语言设置 // 重新初始化ASR服务以应用新的语言设置
val success = azureAsrHelper?.initialize( val asrSuccess = azureAsrHelper?.initialize(
subscriptionKey = this.azureSpeechKey, subscriptionKey = this.azureSpeechKey,
region = this.azureSpeechRegion, region = this.azureSpeechRegion,
supportedLanguages = languages.toTypedArray(), supportedLanguages = languages.toTypedArray(),
audioSourceType = audioSourceType audioSourceType = audioSourceType
) ?: false ) ?: false
if (success) { // 同时更新TTS语言设置
Log.i(TAG, "语言设置更新成功") val ttsSuccess = updateTtsLanguage(languages.first()) // 使用第一个语言作为TTS语言
val overallSuccess = asrSuccess && ttsSuccess
if (overallSuccess) {
Log.i(TAG, "ASR和TTS语言设置更新成功")
sendEvent("languages_updated", mapOf( sendEvent("languages_updated", mapOf(
"languages" to languages, "languages" to languages,
"status" to "success" "status" to "success",
"asr_success" to asrSuccess,
"tts_success" to ttsSuccess
)) ))
} else { } else {
Log.e(TAG, "语言设置更新失败") Log.e(TAG, "语言设置更新失败 - ASR: $asrSuccess, TTS: $ttsSuccess")
sendEvent("error", mapOf( sendEvent("error", mapOf(
"code" to "LANGUAGE_UPDATE_FAILED", "code" to "LANGUAGE_UPDATE_FAILED",
"message" to "语言设置更新失败" "message" to "语言设置更新失败 - ASR: $asrSuccess, TTS: $ttsSuccess"
)) ))
} }
success overallSuccess
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "设置支持语言异常: ${e.message}") Log.e(TAG, "设置支持语言异常: ${e.message}")
sendEvent("error", mapOf( sendEvent("error", mapOf(
@ -1166,6 +1173,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"
}
}
}
/** /**
* 保存聊天消息 * 保存聊天消息
*/ */

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

@ -291,8 +291,8 @@ class AgentServiceImpl: NSObject {
// 使用更安全的方式检查self是否存在 // 使用更安全的方式检查self是否存在
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
guard let self = self else { return } guard let self = self else { return }
if self.isRecognizing && !self.hasSpeechDetected && !self.isSpeaking && !self.isAiStreaming { if self.isRecognizing && !self.isSpeaking && !self.isAiStreaming {
self.stopRecognition() self.stopRecognition()
self.sendEvent(name: "auto_stop", data: [ self.sendEvent(name: "auto_stop", data: [
"reason": "idle_timeout", "reason": "idle_timeout",
@ -349,8 +349,14 @@ print("ai启动语音=\(audioSourceType)=\(useBle)")
return true return true
} }
func recognizeCallback(useBle: Bool = false) -> Bool { func recognizeCallback() -> Bool {
print("重新设置识别回调=")
guard let asrsetupSuccess = azureAsrHelper?.setupEventListeners(
callback: self
), asrsetupSuccess else {
sendError("初始化语音识别服务失败", code: "ASR_INIT_ERROR")
return false
}
return true return true
} }
@ -365,7 +371,12 @@ print("ai启动语音=\(audioSourceType)=\(useBle)")
return true return true
} }
func disableBluetoothAudio() {
azureAsrHelper?.disableBluetoothAudio()
}
func restoreOriginalAudioState() {
azureAsrHelper?.restoreOriginalAudioState()
}
func pushAudioData(_ audioData: Data) -> Bool { func pushAudioData(_ audioData: Data) -> Bool {
if !isRecognizing || !isInitialized { if !isRecognizing || !isInitialized {
return false return false
@ -890,20 +901,20 @@ private func jsonToString(_ json: [String: Any]) -> String? {
} }
/** /**
* 设置支持的语音识别语言 * 设置支持的语音识别语言,同时更新TTS语言设置
* *
* @param languages 支持的语言列表,如 ["zh-CN", "en-US"] * @param languages 支持的语言列表,如 ["zh-CN", "en-US"]
* @return 是否设置成功 * @return 是否设置成功
*/ */
func setSupportedLanguages(_ languages: [String]) -> Bool { func setSupportedLanguages(_ languages: [String]) -> Bool {
os_log("设置支持的语言: %{public}@", log: logger, type: .info, languages.joined(separator: ", ")) os_log("设置支持的语言: %{public}@", log: logger, type: .info, languages.joined(separator: ", "))
if !isInitialized { if !isInitialized {
os_log("服务未初始化,无法设置语言", log: logger, type: .error) os_log("服务未初始化,无法设置语言", log: logger, type: .error)
sendError("服务未初始化", code: "NOT_INITIALIZED") sendError("服务未初始化", code: "NOT_INITIALIZED")
return false return false
} }
if languages.isEmpty { if languages.isEmpty {
os_log("语言列表为空,使用默认语言", log: logger, type: .info) os_log("语言列表为空,使用默认语言", log: logger, type: .info)
return false return false
@ -916,29 +927,170 @@ private func jsonToString(_ json: [String: Any]) -> String? {
if wasRecognizing { if wasRecognizing {
_ = stopRecognition() _ = stopRecognition()
} }
// 重新初始化ASR引擎,使用新的语言设置 // 重新初始化ASR引擎,使用新的语言设置
guard let success = azureAsrHelper?.initialize( guard let asrSuccess = azureAsrHelper?.initialize(
subscriptionKey: azureSpeechKey, subscriptionKey: azureSpeechKey,
region: azureSpeechRegion, region: azureSpeechRegion,
supportedLanguages: languages, supportedLanguages: languages,
audioSourceType: .microphone audioSourceType: .microphone
), success else { ), asrSuccess else {
os_log("重新初始化ASR引擎失败", log: logger, type: .error) os_log("重新初始化ASR引擎失败", log: logger, type: .error)
sendError("重新初始化ASR引擎失败", code: "ASR_INIT_ERROR") sendError("重新初始化ASR引擎失败", code: "ASR_INIT_ERROR")
return false 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 { if wasRecognizing {
_ = startRecognition() _ = 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 { func dispose() -> Bool {
print("ai释放资源") print("ai释放资源")
if isRecognizing { if isRecognizing {
@ -1431,6 +1583,7 @@ extension AgentServiceImpl: AzureAsrHelper.ContinuousRecognizeCallback {
hasSpeechDetected = true hasSpeechDetected = true
if !previousHasSpeech { if !previousHasSpeech {
print("onRecognizing restartIdleCheck")
restartIdleCheck() restartIdleCheck()
} }

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

@ -51,7 +51,14 @@ public class AgentServicePlugin: NSObject, FlutterPlugin {
case "stopConversation": case "stopConversation":
impl.stopRecognition() impl.stopRecognition()
result(true) result(true)
case "disableBluetoothAudio":
impl.disableBluetoothAudio();
return
case "restoreOriginalAudioState":
impl.restoreOriginalAudioState();
return
case "processTextInput": case "processTextInput":
guard let arguments = call.arguments as? [String: Any], guard let arguments = call.arguments as? [String: Any],
let text = arguments["text"] as? String else { let text = arguments["text"] as? String else {

29
local_plugins/agent_service/lib/agent_service.dart

@ -261,6 +261,33 @@ class AgentService {
} }
} }
/// 恢复原始音频状态
///
/// 恢复原始音频状态
/// 返回是否成功恢复原始音频状态
static Future<bool> restoreOriginalAudioState() async {
try {
final bool result =
await _channel.invokeMethod('restoreOriginalAudioState');
return result;
} on PlatformException catch (e) {
throw AgentServiceException(e.code, e.message ?? '恢复原始音频状态失败', e.details);
}
}
/// 禁用蓝牙音频
///
/// 禁用蓝牙音频
/// 返回是否成功禁用蓝牙音频
static Future<bool> disableBluetoothAudio() async {
try {
final bool result = await _channel.invokeMethod('disableBluetoothAudio');
return result;
} on PlatformException catch (e) {
throw AgentServiceException(e.code, e.message ?? '禁用蓝牙音频失败', e.details);
}
}
/// 停止对话 /// 停止对话
/// ///
/// 停止语音识别 /// 停止语音识别
@ -332,8 +359,6 @@ class AgentService {
} }
} }
/// 停止语音合成 /// 停止语音合成
static Future<bool> stopAiSteam() async { static Future<bool> stopAiSteam() async {
try { try {

2
local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt

@ -745,7 +745,7 @@ class AzureAsrHelper(private val context: Context) {
*/ */
fun saveAudioDataTo(buffer: ByteArray) { fun saveAudioDataTo(buffer: ByteArray) {
if (audioSourceType == AudioSourceType.MICROPHONE) return if (audioSourceType == AudioSourceType.MICROPHONE||!isContinuousRecognitionActive) return
// 放入队列,由写线程写入 // 放入队列,由写线程写入
writeQueue.offer(buffer.copyOf()) writeQueue.offer(buffer.copyOf())

36
local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift

@ -131,7 +131,7 @@ public class AzureAsrHelper: NSObject {
*/ */
private func preInitializeAudioComponents() { private func preInitializeAudioComponents() {
if audioStream == nil { if audioStream == nil {
audioStream = AudioStream() audioStream = AudioStream(parentHelper: self)
audioStream?.initAudioRecord() audioStream?.initAudioRecord()
audioStream?.onAudioData = { [weak self] data in audioStream?.onAudioData = { [weak self] data in
self?.recordfile?.saveAudioDataToWav(data) self?.recordfile?.saveAudioDataToWav(data)
@ -354,7 +354,7 @@ public class AzureAsrHelper: NSObject {
do { do {
// 【优化】检查是否已经初始化,避免重复创建 // 【优化】检查是否已经初始化,避免重复创建
if audioStream == nil { if audioStream == nil {
audioStream = AudioStream() audioStream = AudioStream(parentHelper: self)
audioStream?.initAudioRecord() audioStream?.initAudioRecord()
audioStream?.onAudioData = { [weak self] data in audioStream?.onAudioData = { [weak self] data in
self?.recordfile?.saveAudioDataToWav(data) self?.recordfile?.saveAudioDataToWav(data)
@ -548,6 +548,7 @@ public class AzureAsrHelper: NSObject {
public func disableBluetoothAudio() { public func disableBluetoothAudio() {
do { do {
print("禁用蓝牙音频功能,切换回正常音频模式")
audioStream?.setAudioOutputRoute(.speaker) audioStream?.setAudioOutputRoute(.speaker)
} catch { } catch {
print("disableBluetoothAudio") print("disableBluetoothAudio")
@ -559,6 +560,7 @@ public class AzureAsrHelper: NSObject {
public func restoreOriginalAudioState() { public func restoreOriginalAudioState() {
do { do {
print("恢复原始音频设备状态(通常是重新启用蓝牙)")
audioStream?.setAudioOutputRoute(.receiver) audioStream?.setAudioOutputRoute(.receiver)
} catch { } catch {
print("restoreOriginalAudioState") print("restoreOriginalAudioState")
@ -632,9 +634,18 @@ public class AzureAsrHelper: NSObject {
private var isRunning = false private var isRunning = false
public var isWriting = false public var isWriting = false
private let bufferSize: Int = 4096 private let bufferSize: Int = 4096
private var writeThread: DispatchQueue? private var writeThread: DispatchQueue?
private var currentRoute: AudioOutputRoute? private var currentRoute: AudioOutputRoute?
public var onAudioData: ((Data) -> Void)? public var onAudioData: ((Data) -> Void)?
// 添加对外部类的弱引用
private weak var parentHelper: AzureAsrHelper?
// 添加初始化方法,接收外部类引用
init(parentHelper: AzureAsrHelper) {
self.parentHelper = parentHelper
super.init()
}
/** /**
* 初始化 * 初始化
*/ */
@ -654,7 +665,7 @@ public class AzureAsrHelper: NSObject {
// 创建新的写线程 // 创建新的写线程
writeThread = DispatchQueue(label: "audio.stream.writer") writeThread = DispatchQueue(label: "audio.stream.writer")
startWriteThread() startWriteThread()
self.setAudioOutputRoute(.receiver) //self.setAudioOutputRoute(.receiver)
} }
/// 获取最佳音频格式 (iOS 通常支持标准采样率) /// 获取最佳音频格式 (iOS 通常支持标准采样率)
@ -717,7 +728,8 @@ public class AzureAsrHelper: NSObject {
* @param data 音频数据字节数组 * @param data 音频数据字节数组
*/ */
public func saveAudioDataTo(data: Data) { public func saveAudioDataTo(data: Data) {
if audioSourceType != .external { // 通过父类引用调用方法
if audioSourceType != .external || !(parentHelper?.isContinuousRecognitionActive() ?? false) {
return return
} }
// print("外部data=\(data)") // print("外部data=\(data)")
@ -879,8 +891,7 @@ private func runMicrophoneCapture() {
case bluetooth case bluetooth
} }
public func setAudioOutputRoute(_ route: AudioOutputRoute) { public func setAudioOutputRoute(_ route: AudioOutputRoute) {
guard currentRoute != route else { return }
print("setAudioOutputRoute")
do { do {
// //
if self.isWriting { if self.isWriting {
@ -890,14 +901,15 @@ private func runMicrophoneCapture() {
try audioSession.setActive(false) try audioSession.setActive(false)
} }
print("setAudioOutputRoutecurrent,route=\(route)")
switch route { switch route {
case .speaker: case .speaker:
print("setAudioOutputRoutecurrent,speaker")
// 使用扬声器时必须用videoChat模式 // 使用扬声器时必须用videoChat模式
try audioSession.setCategory( try audioSession.setCategory(
.playAndRecord, .playAndRecord,
mode: .videoChat, mode: .videoChat,
options: [.allowBluetooth, .mixWithOthers, .defaultToSpeaker] options: [.defaultToSpeaker]
) )
try audioSession.overrideOutputAudioPort(.speaker) try audioSession.overrideOutputAudioPort(.speaker)
@ -906,7 +918,7 @@ private func runMicrophoneCapture() {
try audioSession.setCategory( try audioSession.setCategory(
.playAndRecord, .playAndRecord,
mode: .voiceChat, mode: .voiceChat,
options: [.allowBluetooth, .mixWithOthers, .defaultToSpeaker] options: [.allowBluetooth]
) )
try audioSession.overrideOutputAudioPort(.none) try audioSession.overrideOutputAudioPort(.none)

15
local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureSpeechPlugin.swift

@ -184,6 +184,15 @@ import os.log
callback: currentAsrCallback! callback: currentAsrCallback!
) )
result(success) result(success)
case "disableBluetoothAudio":
azureAsrHelper.disableBluetoothAudio();
return
case "restoreOriginalAudioState":
azureAsrHelper.restoreOriginalAudioState();
return
case "stopContinuousRecognition": case "stopContinuousRecognition":
let success = azureAsrHelper.stopContinuousRecognition() let success = azureAsrHelper.stopContinuousRecognition()
@ -224,7 +233,7 @@ import os.log
return return
} }
do { do {
print("音频文件名称为: \(filePath)") print("音频文件名称为: (filePath)")
try azureAsrHelper.renameFile(filePath: filePath, newName: newName) try azureAsrHelper.renameFile(filePath: filePath, newName: newName)
result(true) result(true)
} catch { } catch {
@ -239,7 +248,7 @@ import os.log
return return
} }
do { do {
print("音频文件名称为: \(sourcePath)") print("音频文件名称为: (sourcePath)")
try azureAsrHelper.moveFile(sourcePath: sourcePath, destPath: destPath) try azureAsrHelper.moveFile(sourcePath: sourcePath, destPath: destPath)
result(true) result(true)
} catch { } catch {
@ -253,7 +262,7 @@ import os.log
return return
} }
do { do {
print("音频文件名称为: \(filePath)") print("音频文件名称为: (filePath)")
try azureAsrHelper.enableRecord(filePath: filePath) try azureAsrHelper.enableRecord(filePath: filePath)
result(true) result(true)
} catch { } catch {

Loading…
Cancel
Save