Browse Source

加入通话翻译,音视频翻译

weicu
fdp 1 year ago
parent
commit
75a6310381
  1. 2
      lib/core/translations/language/zh_cn.dart
  2. 5
      lib/data/services/asr_service.dart
  3. 60
      lib/data/services/speech_impl/azure_asr_service.dart
  4. 3
      lib/data/services/speech_impl/flutter_asr_service.dart
  5. 5
      lib/data/services/speech_impl/volcano_asr_api_service.dart
  6. 133
      lib/data/services/speech_impl/volcano_asr_service.dart
  7. 6
      lib/data/services/speech_impl/xunfei_asr_service.dart
  8. 38
      lib/modules/translation/controllers/translation_controller.dart
  9. 68
      lib/modules/translation/views/translation_view.dart
  10. 11
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt
  11. 13
      local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureSpeechPlugin.kt
  12. 4
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleConst.kt
  13. 4
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt

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

@ -117,6 +117,8 @@ const Map<String, String> zhCN = {
"allRoundAssistant": "全能助理",
"allRoundAssistantInfo": "帮你解决各类问题",
"simultaneousTranslation": "同声翻译",
'audioVideoTranslation': '音视频翻译',
'callTranslation': '通话翻译',
"simultaneousTranslationInfo": "实时翻译80+种语言",
"aiGirlfriend": "亲子陪伴",
"aiGirlfriendInfo": "智能陪伴,随时聊天",

5
lib/data/services/asr_service.dart

@ -16,11 +16,6 @@ abstract class AsrService {
/// 返回识别的最终结果
Future<RecognitionEvent> recognizeOnce();
/// 开始连续语音识别
///
/// 推送音频数据到连续识别中
Future<void> pushAudioData(Uint8List data);
/// 开始连续语音识别
///
/// 返回一个包含识别事件的流

60
lib/data/services/speech_impl/azure_asr_service.dart

@ -8,8 +8,8 @@ import '../asr_service.dart';
/// 音频源类型
enum AudioSourceType {
microphone, // 使用设备麦克风
external // 使用外部提供的音频数据
microphone, // 使用设备麦克风
external // 使用外部提供的音频数据
}
/// Azure 语音识别服务
@ -18,7 +18,8 @@ enum AudioSourceType {
class AzureAsrService extends GetxService implements AsrService {
static final AzureAsrService to = Get.put(AzureAsrService());
static const MethodChannel _channel = MethodChannel('azure_speech/asr');
static const EventChannel _eventChannel = EventChannel('azure_speech/asr_events');
static const EventChannel _eventChannel =
EventChannel('azure_speech/asr_events');
final GetStorage _storage = GetStorage();
bool _isInitialized = false;
late final String _subscriptionKey;
@ -77,7 +78,8 @@ class AzureAsrService extends GetxService implements AsrService {
bool useEchoCancellation = false,
}) async {
try {
final List<String> languages = supportedLanguages ?? _defaultSupportedLanguages;
final List<String> languages =
supportedLanguages ?? _defaultSupportedLanguages;
// 检查是否需要重新初始化
if (_isInitialized) {
@ -85,7 +87,9 @@ class AzureAsrService extends GetxService implements AsrService {
}
// 设置音频源类型
_audioSourceType = useExternalAudio ? AudioSourceType.external : AudioSourceType.microphone;
_audioSourceType = useExternalAudio
? AudioSourceType.external
: AudioSourceType.microphone;
final bool result = await _channel.invokeMethod('initialize', {
'subscriptionKey': _subscriptionKey,
@ -94,7 +98,7 @@ class AzureAsrService extends GetxService implements AsrService {
'audioSourceType': _audioSourceType.toString().split('.').last,
'useEchoCancellation': useEchoCancellation,
});
_setupEventChannel();
_isInitialized = result;
@ -120,8 +124,9 @@ class AzureAsrService extends GetxService implements AsrService {
if (result is Map) {
// 处理新格式返回
final String text = result['text'] as String? ?? '';
final String detectedLanguage = result['detectedLanguage'] as String? ?? '';
final String detectedLanguage =
result['detectedLanguage'] as String? ?? '';
// 更新最新识别结果
_latestRecognizedText = text;
_latestDetectedLanguage = detectedLanguage;
@ -157,7 +162,8 @@ class AzureAsrService extends GetxService implements AsrService {
_eventStreamController = StreamController<RecognitionEvent>.broadcast();
// 开始连续识别
final bool result = await _channel.invokeMethod('startContinuousRecognition');
final bool result =
await _channel.invokeMethod('startContinuousRecognition');
if (!result) {
_cleanupEventStream();
@ -180,7 +186,8 @@ class AzureAsrService extends GetxService implements AsrService {
}
try {
final bool result = await _channel.invokeMethod('stopContinuousRecognition');
final bool result =
await _channel.invokeMethod('stopContinuousRecognition');
_cleanupEventStream();
return result;
} catch (e) {
@ -208,7 +215,8 @@ class AzureAsrService extends GetxService implements AsrService {
switch (eventType) {
case 'result':
final String text = eventMap['text'] as String? ?? '';
final String detectedLanguage = eventMap['detectedLanguage'] as String? ?? '';
final String detectedLanguage =
eventMap['detectedLanguage'] as String? ?? '';
_latestRecognizedText = text;
_latestDetectedLanguage = detectedLanguage;
_eventStreamController?.add(RecognitionEvent(
@ -220,7 +228,8 @@ class AzureAsrService extends GetxService implements AsrService {
case 'recognizing':
final String text = eventMap['text'] as String? ?? '';
final String detectedLanguage = eventMap['detectedLanguage'] as String? ?? '';
final String detectedLanguage =
eventMap['detectedLanguage'] as String? ?? '';
_eventStreamController?.add(RecognitionEvent(
type: RecognitionEventType.intermediateResult,
text: text,
@ -303,31 +312,4 @@ class AzureAsrService extends GetxService implements AsrService {
_isInitialized = false;
}
}
@override
Future<bool> pushAudioData(Uint8List data) async {
if (!_isInitialized) {
await initialize(useExternalAudio: true);
}
if (_audioSourceType != AudioSourceType.external) {
Logger.error('当前非外部音频模式,不能推送音频数据');
return false;
}
try {
final bool result = await _channel.invokeMethod('pushAudioData', {
'audioData': data // Flutter 会自动将 Uint8List 转换为 ByteBuffer
});
if (!result) {
throw Exception('推送音频数据失败');
}
return true;
} catch (e) {
Logger.error('推送音频数据失败: $e');
return false;
}
}
}

3
lib/data/services/speech_impl/flutter_asr_service.dart

@ -474,7 +474,4 @@ class FlutterAsrService extends GetxService implements AsrService {
Logger.error('释放语音识别资源失败: $e');
}
}
@override
Future<void> pushAudioData(Uint8List data) async {}
}

5
lib/data/services/speech_impl/volcano_asr_api_service.dart

@ -809,9 +809,4 @@ class VolcanoAsrApiService implements AsrService {
_isContinuousRecognitionActive = false;
_isRecognitionEndRequested = false;
}
@override
Future<void> pushAudioData(Uint8List data) {
throw UnimplementedError();
}
}

133
lib/data/services/speech_impl/volcano_asr_service.dart

@ -8,35 +8,36 @@ import 'package:flutter_dotenv/flutter_dotenv.dart';
import '../asr_service.dart';
/// 火山语音识别服务实现
class VolcanoAsrService extends GetxService implements AsrService {
class VolcanoAsrService extends GetxService implements AsrService {
/// 方法通道
static const MethodChannel _channel = MethodChannel('volcano_speech/asr');
/// 事件通道
static const EventChannel _eventChannel = EventChannel('volcano_speech/asr_events');
static const EventChannel _eventChannel =
EventChannel('volcano_speech/asr_events');
/// 事件流控制器
final StreamController<RecognitionEvent> _eventStreamController =
final StreamController<RecognitionEvent> _eventStreamController =
StreamController<RecognitionEvent>.broadcast();
/// 事件订阅
StreamSubscription? _eventSubscription;
/// 是否初始化完成
bool _isInitialized = false;
/// 是否正在连续识别
bool _isContinuousRecognitionActive = false;
/// 当前语言
String _currentLanguage = 'zh-CN';
/// 支持的语言列表
final List<String> _supportedLanguages = ['zh-CN', 'en-US'];
@override
List<String> get supportedLanguages => _supportedLanguages;
/// 构造函数
VolcanoAsrService() {
_setupEventListener();
@ -48,39 +49,42 @@ class VolcanoAsrService extends GetxService implements AsrService {
List<String>? supportedLanguages,
}) async {
if (_isInitialized) return true;
try {
// 如果提供了支持的语言列表,则替换默认列表
if (supportedLanguages != null && supportedLanguages.isNotEmpty) {
_supportedLanguages.clear();
_supportedLanguages.addAll(supportedLanguages);
}
// 从.env文件读取配置
final appId = dotenv.env['VOLCANO_SPEECH_APP_ID'] ?? '';
final appToken = dotenv.env['VOLCANO_SPEECH_APP_TOKEN'] ?? '';
final resourceId = dotenv.env['VOLCANO_ASR_RESOURCE_ID'] ?? 'volc.bigasr.sauc.duration';
final resourceId =
dotenv.env['VOLCANO_ASR_RESOURCE_ID'] ?? 'volc.bigasr.sauc.duration';
if (appId.isEmpty || appToken.isEmpty) {
debugPrint('火山语音初始化失败:缺少VOLCANO_SPEECH_APP_ID或VOLCANO_SPEECH_APP_TOKEN环境变量');
debugPrint(
'火山语音初始化失败:缺少VOLCANO_SPEECH_APP_ID或VOLCANO_SPEECH_APP_TOKEN环境变量');
return false;
}
// 初始化火山语音ASR
final initResult = await _channel.invokeMethod<bool>('initialize', {
'appId': appId,
'appToken': appToken,
'resourceId': resourceId,
}) ?? false;
'appId': appId,
'appToken': appToken,
'resourceId': resourceId,
}) ??
false;
if (!initResult) {
debugPrint('火山语音ASR初始化失败');
return false;
}
// 设置默认语言、VAD参数和启用音量返回
await _configureAsr();
_isInitialized = true;
return true;
} catch (e) {
@ -88,14 +92,13 @@ class VolcanoAsrService extends GetxService implements AsrService {
return false;
}
}
/// 配置ASR参数
Future<void> _configureAsr() async {
// 设置默认语言
await _channel.invokeMethod<bool>('setLanguage', {
'language': _currentLanguage,
});
}
/// 设置事件监听
@ -118,25 +121,25 @@ class VolcanoAsrService extends GetxService implements AsrService {
/// 处理ASR事件
void _handleAsrEvent(Map<String, dynamic> event) {
final eventType = event['type'] as String?;
switch (eventType) {
case 'sessionStarted':
_eventStreamController.add(RecognitionEvent(
type: RecognitionEventType.sessionStarted,
));
break;
case 'sessionStopped':
_eventStreamController.add(RecognitionEvent(
type: RecognitionEventType.sessionStopped,
));
_isContinuousRecognitionActive = false;
break;
case 'recognizing':
final text = event['text'] as String?;
final language = event['language'] as String?;
if (text != null) {
debugPrint('接收到中间识别结果: $text');
_eventStreamController.add(RecognitionEvent(
@ -146,11 +149,11 @@ class VolcanoAsrService extends GetxService implements AsrService {
));
}
break;
case 'result':
final text = event['text'] as String?;
final language = event['language'] as String?;
if (text != null) {
debugPrint('接收到最终识别结果: $text');
_eventStreamController.add(RecognitionEvent(
@ -160,10 +163,10 @@ class VolcanoAsrService extends GetxService implements AsrService {
));
}
break;
case 'error':
final errorMessage = event['message'] as String?;
debugPrint('接收到识别错误: ${errorMessage ?? "未知错误"}');
_eventStreamController.add(RecognitionEvent(
type: RecognitionEventType.error,
@ -171,11 +174,12 @@ class VolcanoAsrService extends GetxService implements AsrService {
));
_isContinuousRecognitionActive = false;
break;
case 'volumeChanged':
// 音量变化事件仅在日志中输出,不发送到事件流
final volume = event['volume'] as int?;
if (volume != null && volume > 5) { // 仅记录有意义的音量变化
if (volume != null && volume > 5) {
// 仅记录有意义的音量变化
debugPrint('音量变化: $volume');
}
break;
@ -199,11 +203,11 @@ class VolcanoAsrService extends GetxService implements AsrService {
error: '语音识别服务未初始化',
);
}
try {
// 创建一个Completer用于等待最终结果
final completer = Completer<RecognitionEvent>();
// 创建一次性事件监听
StreamSubscription? subscription;
subscription = _eventStreamController.stream.listen((event) {
@ -218,10 +222,11 @@ class VolcanoAsrService extends GetxService implements AsrService {
subscription?.cancel();
}
});
// 启动一次性识别
final started = await _channel.invokeMethod<bool>('recognizeOnce') ?? false;
final started =
await _channel.invokeMethod<bool>('recognizeOnce') ?? false;
if (!started) {
subscription.cancel();
return RecognitionEvent(
@ -229,7 +234,7 @@ class VolcanoAsrService extends GetxService implements AsrService {
error: '启动一次性识别失败',
);
}
// 设置超时,防止永久等待
Future.delayed(const Duration(seconds: 30), () {
if (!completer.isCompleted) {
@ -242,7 +247,7 @@ class VolcanoAsrService extends GetxService implements AsrService {
_channel.invokeMethod('stopRecognize');
}
});
// 等待结果
return await completer.future;
} catch (e) {
@ -250,7 +255,7 @@ class VolcanoAsrService extends GetxService implements AsrService {
try {
await _channel.invokeMethod('stopRecognize');
} catch (_) {}
return RecognitionEvent(
type: RecognitionEventType.error,
error: e.toString(),
@ -268,10 +273,12 @@ class VolcanoAsrService extends GetxService implements AsrService {
));
return _eventStreamController.stream;
}
try {
final success = await _channel.invokeMethod<bool>('startContinuousRecognition') ?? false;
final success =
await _channel.invokeMethod<bool>('startContinuousRecognition') ??
false;
if (success) {
_isContinuousRecognitionActive = true;
} else {
@ -286,7 +293,7 @@ class VolcanoAsrService extends GetxService implements AsrService {
error: '启动连续识别异常:$e',
));
}
return _eventStreamController.stream;
}
@ -296,10 +303,12 @@ class VolcanoAsrService extends GetxService implements AsrService {
if (!_isInitialized || !_isContinuousRecognitionActive) {
return false;
}
try {
final success = await _channel.invokeMethod<bool>('stopContinuousRecognition') ?? false;
final success =
await _channel.invokeMethod<bool>('stopContinuousRecognition') ??
false;
if (success) {
_isContinuousRecognitionActive = false;
}
@ -310,18 +319,6 @@ class VolcanoAsrService extends GetxService implements AsrService {
}
}
/// 推送音频数据到连续识别中
@override
Future<void> pushAudioData(Uint8List data) async {
if (!_isInitialized || !_isContinuousRecognitionActive) {
return;
}
// 注意:此功能需要在引擎初始化时设置音频输入类型为stream
// 由于当前实现默认使用麦克风输入,此方法暂时不会实际处理数据
debugPrint('警告:当前火山语音ASR使用麦克风输入,pushAudioData方法不会起作用');
}
/// 检查连续识别是否活跃
@override
bool isContinuousRecognitionActive() {
@ -334,17 +331,17 @@ class VolcanoAsrService extends GetxService implements AsrService {
if (_isContinuousRecognitionActive) {
await stopContinuousRecognition();
}
await _eventSubscription?.cancel();
_eventSubscription = null;
if (!_eventStreamController.isClosed) {
await _eventStreamController.close();
}
if (_isInitialized) {
await _channel.invokeMethod<bool>('release');
_isInitialized = false;
}
}
}
}

6
lib/data/services/speech_impl/xunfei_asr_service.dart

@ -269,10 +269,4 @@ class XunfeiAsrService extends GetxService implements AsrService {
_isInitialized = false;
}
}
@override
Future<void> pushAudioData(Uint8List data) {
// TODO: implement pushAudioData
throw UnimplementedError();
}
}

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

@ -57,10 +57,29 @@ class TranslationController extends GetxController {
final targetLanguageCode = 'en-US'.obs;
var detectedLanguageCode = '';
final currentModeTitle = 'simultaneousTranslation'.obs;
final currentMode = 'simultaneous'.obs;
// 翻译历史和当前项
final translationHistory = <TranslationItem>[].obs;
String? currentSessionId;
void changeTranslationMode(String mode) {
currentMode.value = mode;
currentModeTitle.value = _getModeTitle(mode);
// 这里添加具体模式切换逻辑
}
String _getModeTitle(String mode) {
switch (mode) {
case 'audioVideo':
return 'audioVideoTranslation';
case 'call':
return 'callTranslation';
default:
return 'simultaneousTranslation';
}
}
// 兼容旧版视图的计算属性
Rx<TranslationItem?> get currentItem {
if (translationHistory.isNotEmpty &&
@ -103,7 +122,6 @@ class TranslationController extends GetxController {
targetLanguageCode.value = targetAsrCode;
}
// 加载历史记录
loadTranslationHistory();
@ -116,7 +134,6 @@ class TranslationController extends GetxController {
});
}
// 加载保存的语言设置
void _loadSavedLanguages() {
final savedSourceLanguage = _storage.read(_sourceLanguageKey);
@ -193,9 +210,7 @@ class TranslationController extends GetxController {
Logger.info('开始语音识别');
if (isRecognizing.value) return;
try {
Logger.info('开始语音识别1');
// 每次开始识别前检查 ASR 服务是否已配置为当前语言对
final List<String> currentLanguages = [
sourceLanguageCode.value,
@ -225,15 +240,18 @@ class TranslationController extends GetxController {
// // 添加到历史记录
// translationHistory.add(newItem);
if (currentMode.value == "audioVideo") {
// 发送ble音乐或者通话远端声音
Logger.info('发送ble音乐或者通话远端声音');
} else if (currentMode.value == "call") {
// 发送ble系统mic和dac(音乐或者通话远端)声音
Logger.info('发送ble系统mic和dac(音乐或者通话远端)声音');
}
// 开始连续语音识别
final recognitionStream = await _asrService.startContinuousRecognition();
Logger.info('翻译开启,开始监');
// 监听音频数据
_audioSubscription = jieli.audioDataStream.listen((data) async {
// Logger.info('翻译开启,开始监听解码后的数据=${data}');
final pushAudioDataStream = await _asrService.pushAudioData(data);
});
isRecognizing.value = true;

68
lib/modules/translation/views/translation_view.dart

@ -21,7 +21,20 @@ class TranslationView extends GetView<TranslationController> {
);
}
PreferredSizeWidget _buildAppBar(bool isDarkMode) {
PreferredSizeWidget _buildAppBar(bool isDarkMode) {
// 在同一个类中添加辅助方法
PopupMenuItem<String> _buildPopupMenuItem(String value, String text) {
return PopupMenuItem(
value: value,
child: Text(
text,
style: TextStyle(
color: isDarkMode ? Colors.white : Colors.black,
),
),
);
}
return AppBar(
backgroundColor: isDarkMode ? AppColors.cardBackground : Colors.white,
elevation: 1,
@ -37,14 +50,37 @@ class TranslationView extends GetView<TranslationController> {
Get.back();
},
),
title: Text(
'simultaneousTranslation'.tr,// 实时翻译
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.w600,
color: isDarkMode ? Colors.white : Colors.black,
),
),
title: Obx(() => PopupMenuButton<String>(
onSelected: (value) {
controller.changeTranslationMode(value);
},
color: isDarkMode ? AppColors.cardBackground : Colors.white,
itemBuilder: (BuildContext context) => [
_buildPopupMenuItem('simultaneous', 'simultaneousTranslation'.tr),
_buildPopupMenuItem('audioVideo', 'audioVideoTranslation'.tr),
_buildPopupMenuItem('call', 'callTranslation'.tr),
],
child: Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
controller.currentModeTitle.value.tr, // 添加 .value 访问实际值
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.w600,
color: isDarkMode ? Colors.white : Colors.black,
),
),
Icon(
Icons.arrow_drop_down,
color: isDarkMode ? Colors.white : Colors.black,
size: 20.sp,
),
],
),
),
)),
centerTitle: true,
actions: [
// TTS toggle button
@ -93,7 +129,8 @@ class TranslationView extends GetView<TranslationController> {
child: GestureDetector(
onTap: () => _showLanguageSelectionDialog(true, isDarkMode),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h),
padding:
EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h),
decoration: BoxDecoration(
color: isDarkMode
? Colors.white.withOpacity(0.1)
@ -101,8 +138,8 @@ class TranslationView extends GetView<TranslationController> {
borderRadius: BorderRadius.circular(16.r),
),
child: Obx(() => Text(
'${controller.sourceLanguage.value
.split(' ')[0]}'.tr, // Just show the Chinese part
'${controller.sourceLanguage.value.split(' ')[0]}'
.tr, // Just show the Chinese part
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
@ -140,7 +177,8 @@ class TranslationView extends GetView<TranslationController> {
child: GestureDetector(
onTap: () => _showLanguageSelectionDialog(false, isDarkMode),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h),
padding:
EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h),
decoration: BoxDecoration(
color: isDarkMode
? Colors.white.withOpacity(0.1)
@ -148,8 +186,8 @@ class TranslationView extends GetView<TranslationController> {
borderRadius: BorderRadius.circular(16.r),
),
child: Obx(() => Text(
'${controller.targetLanguage.value
.split(' ')[0]}'.tr, // Just show the English part
'${controller.targetLanguage.value.split(' ')[0]}'
.tr, // Just show the English part
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,

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

@ -287,11 +287,12 @@ object AgentService : CoroutineScope {
if (isRecognitionActive) return true
// 检查录音权限
// if (!checkRecordAudioPermission()) {
// FileLogger.e(TAG, "需要录音权限,请在设置中授予权限")
// sendEvent("error", mapOf("code" to "PERMISSION_DENIED", "message" to "需要录音权限"))
// return false
// }
if (!isExternalActive||!checkRecordAudioPermission()) {
FileLogger.e(TAG, "需要录音权限,请在设置中授予权限")
sendEvent("error", mapOf("code" to "PERMISSION_DENIED", "message" to "需要录音权限"))
return false
}
isRecognitionActive = true
hasSpeechDetected = false

13
local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureSpeechPlugin.kt

@ -143,19 +143,6 @@ class AzureSpeechPlugin: FlutterPlugin, CoroutineScope {
result.error("INITIALIZATION_ERROR", e.message, null)
}
}
"pushAudioData" -> {
val audioData = call.argument<ByteArray>("audioData")
try {
if (audioData != null) {
azureAsrHelper.pushAudioData(audioData)
result.success(true)
} else {
result.error("INVALID_ARGUMENTS", "音频数据不能为空", null)
}
} catch (e: Exception) {
result.error("PUSH_AUDIO_ERROR", e.message, null)
}
}
"recognizeOnce" -> {
val useExternalAudio = call.argument<Boolean>("useExternalAudio") ?: false
try {

4
local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleConst.kt

@ -82,9 +82,9 @@ object BleConst {
// ================== 编解码控制值 ==================
/** 关闭编解码指令 */
const val CODEC_CONTROL_CLOSE = 0x00
/** 打开解码指令 */
/** 音乐或者通话远端声音 */
const val CODEC_CONTROL_DECODE_ON = 0xA1
/** 通过A2DP播放 */
/** mic和dac(音乐或者通话远端)声音 */
const val CODEC_CONTROL_A2DP_PLAY = 0xA2
/** 打开编码指令 */
const val CODEC_CONTROL_ENCODE_ON = 0xB1

4
local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt

@ -907,7 +907,9 @@ object BleService {
FileLogger.i(TAG, "关闭编解码...")
return sendCommand(
BleConst.CMD_CONTROL_CODEC.toByte(),
byteArrayOf(BleConst.CODEC_CONTROL_CLOSE.toByte())
byteArrayOf(BleConst.CODEC_CONTROL_CLOSE.toByte(),
BleConst.AUDIO_CHANNEL_RIGHT.toByte())
)
}

Loading…
Cancel
Save