diff --git a/lan/lan_hi.json b/lan/lan_hi.json index 070bc036..3a12b142 100644 --- a/lan/lan_hi.json +++ b/lan/lan_hi.json @@ -245,7 +245,7 @@ "摸亮触摸屏": "कीपैड को सक्रिय करने के लिए किसी भी कुंजी को स्पर्श करें", "摸亮触摸屏,锁进入可添加状态,点击下一步": "लॉक को सक्रिय करने के लिए कृपया किसी भी कुंजी को स्पर्श करें और उसे युग्मन मोड में रखें। अगला दबाओ", "附近的锁": "पास के ताले", - "如需修改名字请重新命名,点击确定添加锁": "यदि आप नाम बदलना चाहते हैं, तो कृपया नाम बदलें, लॉक जोड़ने के लिए OK पर क्लिक करें।", + "如需修改名字请重新命名,点击确定添加锁": "यदि आपको नाम बदलने की जरूरत है, कृपया इसे नाम बदलें और ताला जोड़ने के लिए ठीक है क्लिक करें", "添加锁时,手机必须在锁旁边": "लॉक जोड़ते समय, फ़ोन लॉक के बगल में होना चाहिए।", "登录": "प्रवेश करना", "注册": "रजिस्टर", @@ -1044,42 +1044,42 @@ "导出锁数据": "निर्यात लॉक डेटा", "一键开锁": "एक क्लिक अनलॉक", "已开通": "खुल गया", - "英文": "अंग्रेज़ी", - "简体中文": "पर", - "繁体中文": "पर", - "法语": "फ़्रैंकैइस", - "俄语": "सांद्रता सांद्रता सांद्रता", - "德语": "काटकर अलग कर देना", - "日语": "सोच।", - "韩语": "सोच।", - "意大利语": "इटालियनो", - "乌克兰语": "टाइप टाइप टाइप", - "葡萄牙语": "पुर्तगाओं", - "西班牙语": "स्पैनिश", - "阿拉伯语": "ए ए सेंटर सेंटर सेंटर सेंटर सेंटर।", - "越南语": "तिवारी पॅटर्न Vi पेंट टी", - "马来语": "बाहास मेलायु", - "荷兰语": "नेडरलैंड्स", - "罗马尼亚语": "रोमन पैन", - "立陶宛语": "लिटूवी हदीन", - "瑞典语": "स्का", - "爱沙尼亚语": "ईस्टी", - "波兰语": "पोल्की", - "斯洛伐克语": "चेपक", - "捷克语": "पुरआपसे", - "希腊语": "मोम मोम निर्देशिकाएँ", - "希伯来语": "।", - "塞尔维亚语": "टाइप टाइप", - "土耳其语": "तुरकी", - "匈牙利语": "माँजू", - "保加利亚语": "टाइप ने वाई", - "哈萨克斯坦语": "और और और और", - "孟加拉语": "कितना पैसा", - "克罗地亚语": "ह्रवत्स्की", - "泰语": "सोच।", - "印度尼西亚语": "बाहसा इंडोनेशिया", - "芬兰语": "सूमी", - "丹麦语": "मूर्ख व्यक्ति", + "英文": "English", + "简体中文": "简体中文", + "繁体中文": "繁體中文", + "法语": "Français", + "俄语": "Русский", + "德语": "Deutsch", + "日语": "日本語", + "韩语": "한국어", + "意大利语": "Italiano", + "乌克兰语": "Українська", + "葡萄牙语": "Português", + "西班牙语": "Español", + "阿拉伯语": "العربية", + "越南语": "Tiếng Việt", + "马来语": "Bahasa Melayu", + "荷兰语": "Nederlands", + "罗马尼亚语": "Română", + "立陶宛语": "Lietuvių", + "瑞典语": "Svenska", + "爱沙尼亚语": "Eesti", + "波兰语": "Polski", + "斯洛伐克语": "Slovenčina", + "捷克语": "Čeština", + "希腊语": "Ελληνικά", + "希伯来语": "עברית", + "塞尔维亚语": "Српски", + "土耳其语": "Türkçe", + "匈牙利语": "Magyar", + "保加利亚语": "Български", + "哈萨克斯坦语": "Қазақ", + "孟加拉语": "বাংলা", + "克罗地亚语": "Hrvatski", + "泰语": "ไทย", + "印度尼西亚语": "Bahasa Indonesia", + "芬兰语": "Suomi", + "丹麦语": "Dansk", "印地语": "हिंदीName", "乌尔都语": "اوردو", "重置后,该锁的掌静脉都将被删除哦,确认要重置吗?": "रीसेट के बाद, लॉक के मेटाकार्पल नसों को हटा दिया जाएगा। क्या आप निश्चित रूप से रीसेट करना चाहते हैं?", @@ -1097,8 +1097,8 @@ "支持的国家值": "यूएसए, कनाडा, यूके, ऑस्ट्रेलिया, भारत, जर्मनी, जर्मनी, इटली, स्पेन, जापान", "操作流程": "ऑपरेशन प्रक्रिया", "操作流程值": "1 स्मार्ट लॉक ऐप के साथ लॉक और गेटवे जोड़ें\n\n2 एप्लिकेशन में लॉक का रिमोट अनलॉकिंग फ़ंक्शन सक्षम करें (यह फ़ंक्शन डिफ़ॉल्ट रूप से बंद है). अगर आपके पास यह विकल्प नहीं है, लॉक एलेक्सा का समर्थन नहीं करता है\n\n3 एलेक्सा के लिए कौशल जोड़ें और उन्हें स्मार्ट लॉक ऐप के खाते और पासवर्ड के साथ अधिकृत करें। प्राधिकरण सफल होने के बाद, आप खाते के अंतर्गत उपकरणों की खोज कर सकते हैं।\n\n4 एलेक्सए ऐप में लॉक का पता लगाएं, वॉयस अनलॉक फ़ंक्शन चालू करें, और भाषा का पासवर्ड सेट करें।\n\n5 लॉक को एलेक्सा के माध्यम से संचालित किया जा सकता है", - "Google Home": "गूगल होम", - "Action name": "क्रिया नाम", + "Google Home": "Google Home", + "Action name": "Action name", "ScienerSmart": "विज्ञानचतुर", "支持的语言": "समर्थित भाषाएं", "英语": "अंग्रेज़ी", diff --git a/lan/lan_hk.json b/lan/lan_hk.json index bf486ffa..24d17e4e 100644 --- a/lan/lan_hk.json +++ b/lan/lan_hk.json @@ -1043,6 +1043,7 @@ "一键开锁": "一鍵解鎖", "已开通": "打開", "英文": "English", + "英文": "English", "简体中文": "简体中文", "繁体中文": "繁體中文", "法语": "Français", diff --git a/lan/lan_ur.json b/lan/lan_ur.json index 528ad145..016ab0b3 100644 --- a/lan/lan_ur.json +++ b/lan/lan_ur.json @@ -1044,23 +1044,23 @@ "导出锁数据": "ایکسپورٹ لاک ڈیٹا", "一键开锁": "ایک کلک ان لاک", "已开通": "کھولی", - "英文": "انگريزی", - "简体中文": "ساده چینی", - "繁体中文": "سنتی چینی", - "法语": "فرانسوی", - "俄语": "روسی", - "德语": "جرمن", - "日语": "جاپانی", - "韩语": "کوریا", - "意大利语": "ایتالیایی", - "乌克兰语": "Ukrainian", + "英文": "English", + "简体中文": "简体中文", + "繁体中文": "繁體中文", + "法语": "Français", + "俄语": "Русский", + "德语": "Deutsch", + "日语": "日本語", + "韩语": "한국어", + "意大利语": "Italiano", + "乌克兰语": "Українська", "葡萄牙语": "Português", "西班牙语": "Español", "阿拉伯语": "العربية", "越南语": "Tiếng Việt", "马来语": "Bahasa Melayu", "荷兰语": "Nederlands", - "罗马尼亚语": "Romunaă", + "罗马尼亚语": "Română", "立陶宛语": "Lietuvių", "瑞典语": "Svenska", "爱沙尼亚语": "Eesti", diff --git a/lan/lan_zh.json b/lan/lan_zh.json index b9a66d9f..47f94f0c 100755 --- a/lan/lan_zh.json +++ b/lan/lan_zh.json @@ -1163,5 +1163,6 @@ "国家地区的选择将影响数据安全,你当前选择的是": "国家地区的选择将影响数据安全,你当前选择的是", "请确认后再继续": "请确认后再继续", "需要相机权限": "需要相机权限", + "一键登录": "一键登录", "网关添加成功": "网关添加成功" } diff --git a/lib/login/login/starLock_login_page.dart b/lib/login/login/starLock_login_page.dart index 1875d334..f9a46982 100755 --- a/lib/login/login/starLock_login_page.dart +++ b/lib/login/login/starLock_login_page.dart @@ -283,7 +283,7 @@ class _StarLockLoginPageState extends State { width: 10.sp, )), Obx(() => Visibility( - visible: state.isCheckVerifyEnable.value, + visible: state.isCheckVerifyEnable.value && state.currentLanguage == 'zh_CN', child: GestureDetector( child: SizedBox( // width: 150.w, diff --git a/lib/login/login/starLock_login_state.dart b/lib/login/login/starLock_login_state.dart index ec224880..b623b61d 100755 --- a/lib/login/login/starLock_login_state.dart +++ b/lib/login/login/starLock_login_state.dart @@ -46,7 +46,8 @@ class StarLockLoginState { }.obs; RxBool isCheckVerifyEnable = false.obs; - + RxString currentLanguage = + CurrentLocaleTool.getCurrentLocaleString().obs; // 当前选择语言 void onClose() { // emailOrPhoneController.dispose(); // pwdController.dispose(); diff --git a/lib/main/lockDetail/lockSet/configuringWifi/configuringWifi/configuringWifi_logic.dart b/lib/main/lockDetail/lockSet/configuringWifi/configuringWifi/configuringWifi_logic.dart index 7a52e347..f4688019 100755 --- a/lib/main/lockDetail/lockSet/configuringWifi/configuringWifi/configuringWifi_logic.dart +++ b/lib/main/lockDetail/lockSet/configuringWifi/configuringWifi/configuringWifi_logic.dart @@ -16,6 +16,7 @@ import 'package:star_lock/blue/io_protocol/io_updataLockSet.dart'; import 'package:star_lock/login/login/entity/LoginEntity.dart'; import 'package:star_lock/main/lockDetail/lockDetail/device_network_info.dart'; import 'package:star_lock/main/lockDetail/lockSet/configuringWifi/configuringWifi/configuringWifiEntity.dart'; +import 'package:star_lock/mine/gateway/addGateway/gatewayConfigurationWifi/gatewayConfigurationWifi_page.dart'; import 'package:star_lock/mine/gateway/addGateway/gatewayConfigurationWifi/getGatewayConfiguration_entity.dart'; import 'package:star_lock/mine/gateway/addGateway/selectGateway/getGatewayInfo_model.dart'; import 'package:star_lock/talk/starChart/entity/star_chart_register_node_entity.dart'; @@ -45,7 +46,7 @@ class ConfiguringWifiLogic extends BaseGetXController { } } - Future updateNetworkInfo({ + void updateNetworkInfo({ required String peerId, required String wifiName, required String secretKey, @@ -82,17 +83,19 @@ class ConfiguringWifiLogic extends BaseGetXController { void _initReplySubscription() { _replySubscription = EventBusManager().eventBus!.on().listen((Reply reply) async { - AppLog.log('收到蓝牙回调${EasyLoading.isShow}'); // WIFI配网结果 if (reply is GatewayConfiguringWifiResultReply) { _replySenderConfiguringWifiResult(reply); } + if (reply is GatewayConfiguringWifiReply) { + _replySenderConfiguringWifiResult(reply); + } if (reply is GatewayGetStatusReply) { _replyGatewayGetStatusReply(reply); } - if (reply is GatewayGetStatusReply) { - _replyStatusInfo(reply); - } + // if (reply is GatewayGetStatusReply) { + // _replyStatusInfo(reply); + // } // 上传数据获取锁设置 if (reply is UpdataLockSetReply) { _replyUpdataLockSetReply(reply); @@ -132,7 +135,7 @@ class ConfiguringWifiLogic extends BaseGetXController { // 保存到缓存 await Storage.saveLockNetWorkInfo(jsonMap); // 上报服务器 - updateNetworkInfo( + updateNetworkInfo( peerId: peerId ?? '', wifiName: wifiName ?? '', secretKey: secretKey ?? '', @@ -482,6 +485,7 @@ class ConfiguringWifiLogic extends BaseGetXController { @override void dispose() { + dismissEasyLoading(); // TODO: implement dispose super.dispose(); state.isLoading.value = false; @@ -489,5 +493,6 @@ class ConfiguringWifiLogic extends BaseGetXController { state.loadingTimer!.cancel(); state.loadingTimer = null; } + } } diff --git a/lib/mine/addLock/saveLock/saveLock_logic.dart b/lib/mine/addLock/saveLock/saveLock_logic.dart index 5b9c3ce3..391ffa4b 100755 --- a/lib/mine/addLock/saveLock/saveLock_logic.dart +++ b/lib/mine/addLock/saveLock/saveLock_logic.dart @@ -486,7 +486,7 @@ class SaveLockLogic extends BaseGetXController { // } void backAction() async { - // eventBus.fire(RefreshLockListInfoDataEvent(clearScanDevices: true)); + // BlueManage().disconnect(); // 查询锁设置信息 @@ -503,6 +503,7 @@ class SaveLockLogic extends BaseGetXController { 'pageName': 'saveLock' }); } else { + eventBus.fire(RefreshLockListInfoDataEvent(clearScanDevices: true,isUnShowLoading: true)); Future.delayed(const Duration(seconds: 1), () { Get.close(state.isFromMap == 1 ? (CommonDataManage().seletLockType == 0 ? 4 : 5) @@ -518,6 +519,7 @@ class SaveLockLogic extends BaseGetXController { }); } } else { + eventBus.fire(RefreshLockListInfoDataEvent(clearScanDevices: true,isUnShowLoading: true)); Future.delayed(const Duration(seconds: 1), () { Get.close(state.isFromMap == 1 ? (CommonDataManage().seletLockType == 0 ? 4 : 5) diff --git a/lib/mine/mineSet/mineSet/mineSet_logic.dart b/lib/mine/mineSet/mineSet/mineSet_logic.dart index 845ec23b..a7b19862 100755 --- a/lib/mine/mineSet/mineSet/mineSet_logic.dart +++ b/lib/mine/mineSet/mineSet/mineSet_logic.dart @@ -143,8 +143,8 @@ class MineSetLogic extends BaseGetXController { // UdpHelp().closeUDP(); logOut(); - StoreService.to.saveLanguageCode(''); - Get.updateLocale(Get.deviceLocale!); + // StoreService.to.saveLanguageCode(''); + // Get.updateLocale(Get.deviceLocale!); BlueManage().disconnect(); StartChartManage().destruction(); diff --git a/lib/talk/starChart/handle/impl/udp_talk_request_handler.dart b/lib/talk/starChart/handle/impl/udp_talk_request_handler.dart index 122cbdc4..06b79ad6 100644 --- a/lib/talk/starChart/handle/impl/udp_talk_request_handler.dart +++ b/lib/talk/starChart/handle/impl/udp_talk_request_handler.dart @@ -6,6 +6,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:get/get.dart'; import 'package:star_lock/appRouters.dart'; +import 'package:star_lock/app_settings/app_settings.dart'; import 'package:star_lock/talk/starChart/constant/message_type_constant.dart'; import 'package:star_lock/talk/starChart/constant/talk_status.dart'; import 'package:star_lock/talk/starChart/entity/scp_message.dart'; @@ -26,13 +27,13 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle @override void handleReq(ScpMessage scpMessage) async { - // 回复成功 - replySuccessMessage(scpMessage); // 判断是否登录账户 final loginData = await Storage.getLoginData(); + // 如果登录账户不为空,且不是被动接听状态,且不是接听成功状态 if (loginData != null && - (talkStatus.status != TalkStatus.passiveCallWaitingAnswer && talkStatus.status!=TalkStatus.answeredSuccessfully)) { + (talkStatus.status != TalkStatus.passiveCallWaitingAnswer || + talkStatus.status != TalkStatus.answeredSuccessfully)) { // 收到对讲请求 final TalkReq talkReq = scpMessage.Payload; startChartManage.FromPeerId = scpMessage.ToPeerId!; @@ -40,6 +41,12 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle startChartManage.lockPeerId = scpMessage.FromPeerId!; // 处理收到接听请求后的事件 _talkRequestEvent(talkObjectName: talkReq.callerName); + + // 回复成功 + replySuccessMessage(scpMessage); + } else { + // 拒绝接听,回复失败 + replyErrorMessage(scpMessage); } } diff --git a/lib/talk/starChart/handle/scp_message_base_handle.dart b/lib/talk/starChart/handle/scp_message_base_handle.dart index f4b68024..11328f46 100644 --- a/lib/talk/starChart/handle/scp_message_base_handle.dart +++ b/lib/talk/starChart/handle/scp_message_base_handle.dart @@ -71,6 +71,15 @@ class ScpMessageBaseHandle { messageId: scpMessage.MessageId!, ); } + // 回复失败消息 + void replyErrorMessage(ScpMessage scpMessage) { + startChartManage.sendGenericRespErrorMessage( + ToPeerId: scpMessage.FromPeerId!, + FromPeerId: scpMessage.ToPeerId!, + PayloadType: scpMessage.PayloadType!, + messageId: scpMessage.MessageId!, + ); + } bool checkGenericRespSuccess(GenericResp genericResp) { if (genericResp == null) return false; diff --git a/lib/talk/starChart/star_chart_manage.dart b/lib/talk/starChart/star_chart_manage.dart index 328e80b8..7f60fc25 100644 --- a/lib/talk/starChart/star_chart_manage.dart +++ b/lib/talk/starChart/star_chart_manage.dart @@ -650,10 +650,15 @@ class StartChartManage { } // 回复失败消息 - void sendGenericRespErrorMessage( - {required String ToPeerId, - required String FromPeerId, - required int PayloadType}) async { + void sendGenericRespErrorMessage({ + required String ToPeerId, + required String FromPeerId, + required int PayloadType, + required int messageId, + }) async { + if (messageId == null) { + messageId = MessageCommand.getNextMessageId(ToPeerId, increment: false); + } final message = MessageCommand.genericRespErrorMessage( ToPeerId: ToPeerId, FromPeerId: FromPeerId, diff --git a/lib/talk/starChart/views/talkView/talk_view_logic.dart b/lib/talk/starChart/views/talkView/talk_view_logic.dart index 7ed217f6..523742c4 100644 --- a/lib/talk/starChart/views/talkView/talk_view_logic.dart +++ b/lib/talk/starChart/views/talkView/talk_view_logic.dart @@ -28,6 +28,7 @@ import 'package:star_lock/talk/starChart/proto/talk_data.pb.dart'; import 'package:star_lock/talk/starChart/proto/talk_expect.pb.dart'; import 'package:star_lock/talk/starChart/star_chart_manage.dart'; import 'package:star_lock/talk/starChart/views/talkView/talk_view_state.dart'; +import 'package:star_lock/tools/G711Tool.dart'; import 'package:star_lock/tools/bugly/bugly_tool.dart'; import '../../../../tools/baseGetXController.dart'; @@ -658,42 +659,20 @@ class TalkViewLogic extends BaseGetXController { // 音频帧处理 Future _onFrame(List frame) async { - // 添加数据监控 - int maxVal = 0; - int minVal = 0; - int sum = 0; - for (int val in frame) { - maxVal = max(maxVal, val); - minVal = min(minVal, val); - sum += val; - } - double average = sum / frame.length; - AppLog.log('音频数据特征 - 最大值: $maxVal, 最小值: $minVal, 平均值: $average'); - - final List processedFrame = preprocessAudio(frame); - // 添加平滑处理 - final List smoothedFrame = smoothAudio(processedFrame); - final List list = listLinearToALaw(smoothedFrame); - _bufferedAudioFrames.addAll(list); + // 首先应用固定增益提升基础音量 + List amplifiedFrame = _applyGain(frame, 1.6); + // 编码为G711数据 + List encodedData = G711Tool.encode(amplifiedFrame, 0); // 0表示A-law + _bufferedAudioFrames.addAll(encodedData); final int ms = DateTime.now().millisecondsSinceEpoch - state.startRecordingAudioTime.value.millisecondsSinceEpoch; - int getFrameLength = state.frameLength; if (Platform.isIOS) { getFrameLength = state.frameLength * 2; } if (_bufferedAudioFrames.length >= getFrameLength) { - // 添加数据监控 - int maxVal = 0; - int minVal = 255; - for (int val in _bufferedAudioFrames) { - maxVal = max(maxVal, val); - minVal = min(minVal, val); - } - AppLog.log( - '发送音频数据 - G711编码后 - 最大值: $maxVal, 最小值: $minVal, 长度: ${_bufferedAudioFrames.length}'); // 发送音频数据到UDP await StartChartManage() .sendTalkDataMessage( @@ -714,177 +693,27 @@ class TalkViewLogic extends BaseGetXController { AppLog.log(error.message!); } - // 简化的音频预处理函数 - List preprocessAudio(List pcmList) { - final List processedList = []; - final int noiseThreshold = 300; // 噪音阈值 +// 添加音频增益处理方法 + List _applyGain(List pcmData, double gainFactor) { + List result = List.filled(pcmData.length, 0); - for (int pcmVal in pcmList) { - // 简单的噪音门控:小于阈值的信号会被减弱 - if (pcmVal.abs() < noiseThreshold) { - pcmVal = (pcmVal * 0.3).round(); // 减弱噪音 + for (int i = 0; i < pcmData.length; i++) { + // PCM数据通常是有符号的16位整数 + int sample = pcmData[i]; + + // 应用增益 + double amplified = sample * gainFactor; + + // 限制在有效范围内,防止溢出 + if (amplified > 32767) { + amplified = 32767; + } else if (amplified < -32768) { + amplified = -32768; } - // 简单的压缩:防止大信号失真 - if (pcmVal.abs() > 20000) { - double factor = 1.0 - ((pcmVal.abs() - 20000) / 12768) * 0.3; - pcmVal = (pcmVal * factor).round(); - } - - processedList.add(pcmVal); + result[i] = amplified.toInt(); } - return processedList; - } - -// 简单的平滑处理 - List smoothAudio(List pcmList) { - final List smoothedList = []; - - for (int i = 0; i < pcmList.length; i++) { - if (i > 0 && i < pcmList.length - 1) { - // 简单的三点平均 - int avg = (pcmList[i - 1] + pcmList[i] * 2 + pcmList[i + 1]) ~/ 4; - smoothedList.add(avg); - } else { - smoothedList.add(pcmList[i]); - } - } - - return smoothedList; - } - - //test测试降噪算法 - // List preprocessAudio(List pcmList) { - // final List processedList = []; - // final int windowSize = 5; - // final int thresholdFactor = 2; // 动态阈值的倍数 - - // for (int i = 0; i < pcmList.length; i++) { - // int pcmVal = pcmList[i]; - - // // 计算当前窗口内的标准差 - // int sum = 0; - // int count = 0; - // for (int j = i; j < i + windowSize && j < pcmList.length; j++) { - // sum += pcmList[j]; - // count++; - // } - // int mean = sum ~/ count; - - // // 计算标准差 - // int varianceSum = 0; - // for (int j = i; j < i + windowSize && j < pcmList.length; j++) { - // varianceSum += (pcmList[j] - mean) * (pcmList[j] - mean); - // } - // double standardDeviation = - // sqrt(varianceSum / count); // Use sqrt from dart:math - - // // 动态阈值 - // int dynamicThreshold = (standardDeviation * thresholdFactor).toInt(); - - // // 动态降噪:如果信号小于动态阈值,则设为0 - // if (pcmVal.abs() < dynamicThreshold) { - // pcmVal = 0; - // } - - // // 移动平均滤波器 - // int sumFilter = 0; - // int countFilter = 0; - // for (int j = i; j < i + windowSize && j < pcmList.length; j++) { - // sumFilter += pcmList[j]; - // countFilter++; - // } - // int average = sumFilter ~/ countFilter; - - // processedList.add(average); - // } - - // return processedList; - // } - -// 简化的音量调整 - List adjustVolume(List pcmList, double volume) { - final List adjustedPcmList = []; - - for (final int pcmVal in pcmList) { - int adjustedPcmVal = (pcmVal * volume).round(); - adjustedPcmVal = adjustedPcmVal.clamp(-32768, 32767); - adjustedPcmList.add(adjustedPcmVal); - } - - return adjustedPcmList; - } - -// 简化的A-law编码 - List listLinearToALaw(List pcmList) { - // 调整音量,使用适中的增益值 - final List adjustedPcmList = adjustVolume(pcmList, 2.2); - - // 执行A-law编码 - final List aLawList = []; - for (final int pcmVal in adjustedPcmList) { - final int aLawVal = linearToALaw(pcmVal); - aLawList.add(aLawVal); - } - - return aLawList; - } - - int linearToALaw(int pcmVal) { - const int alawMax = 0x7FFF; // 32767 - const int alawBias = 0x84; // 132 - - int mask; - int seg; - int aLawVal; - - // Handle sign - if (pcmVal < 0) { - pcmVal = -pcmVal; - mask = 0x7F; // 127 (sign bit is 1) - } else { - mask = 0xFF; // 255 (sign bit is 0) - } - - // Add bias and clamp to ALAW_MAX - pcmVal += alawBias; - if (pcmVal > alawMax) { - pcmVal = alawMax; - } - - // Determine segment - seg = search(pcmVal); - - // Calculate A-law value - if (seg >= 8) { - aLawVal = 0x7F ^ mask; // Clamp to maximum value - } else { - final int quantized = (pcmVal >> (seg + 3)) & 0xF; - aLawVal = (seg << 4) | quantized; - aLawVal ^= 0xD5; // XOR with 0xD5 to match standard A-law table - } - - return aLawVal; - } - - int search(int val) { - final List table = [ - 0xFF, // Segment 0 - 0x1FF, // Segment 1 - 0x3FF, // Segment 2 - 0x7FF, // Segment 3 - 0xFFF, // Segment 4 - 0x1FFF, // Segment 5 - 0x3FFF, // Segment 6 - 0x7FFF // Segment 7 - ]; - const int size = 8; - for (int i = 0; i < size; i++) { - if (val <= table[i]) { - return i; - } - } - return size; + return result; } } diff --git a/lib/tools/G711Tool.dart b/lib/tools/G711Tool.dart new file mode 100644 index 00000000..1d8069a7 --- /dev/null +++ b/lib/tools/G711Tool.dart @@ -0,0 +1,273 @@ +/** + * G711Tool - G.711音频编解码工具类 + * + * G.711是一种用于音频压缩的ITU-T标准,主要用于电话系统。 + * 它有两种主要变体:A-law(主要用于欧洲和国际电话系统)和μ-law(主要用于北美和日本)。 + * + * 该类提供了PCM线性音频数据与G.711 A-law/μ-law格式之间的转换功能。 + * 编码过程将16位线性PCM样本压缩为8位,解码过程则相反。 + */ +class G711Tool { + // 常量定义 + static const int SIGN_BIT = 0x80; // A-law字节的符号位 + static const int QUANT_MASK = 0xf; // 量化字段掩码 + static const int NSEGS = 8; // A-law段数 + static const int SEG_SHIFT = 4; // 段号左移位数 + static const int SEG_MASK = 0x70; // 段字段掩码 + static const int BIAS = 0x84; // 线性编码的偏置值 + + // 查找表 + /** + * μ-law到A-law的转换表 + * 用于在两种编码格式之间进行转换而不需要先解码为线性PCM + */ + static final List _u2a = [ + // u- to A-law conversions + 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 27, 29, 31, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 46, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, + 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128 + ]; + + /** + * A-law到μ-law的转换表 + * 用于在两种编码格式之间进行转换而不需要先解码为线性PCM + */ + static final List _a2u = [ + // A- to u-law conversions + 1, 3, 5, 7, 9, 11, 13, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 32, 33, 33, 34, 34, 35, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 48, 49, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, + 127 + ]; + + /** + * 段结束值表 + * 用于确定PCM值应该映射到哪个段 + * 每个段代表不同的量化步长 + */ + static final List _segEnd = [ + 0xFF, + 0x1FF, + 0x3FF, + 0x7FF, + 0xFFF, + 0x1FFF, + 0x3FFF, + 0x7FFF + ]; + + /** + * 在表中搜索值所属的段 + * + * @param val 要搜索的值 + * @param table 段结束值表 + * @param size 表大小 + * @return 段索引 + */ + static int _search(int val, List table, int size) { + for (int i = 0; i < size; i++) { + if (val <= table[i]) return i; + } + return size; + } + + /** + * 将线性PCM值转换为A-law编码值 + * + * 转换步骤: + * 1. 确定符号位和掩码 + * 2. 对负值进行特殊处理 + * 3. 确定段号 + * 4. 根据段号计算A-law值 + * + * @param pcmVal 16位线性PCM值 + * @return 8位A-law编码值 + */ + static int _linear2alaw(int pcmVal) { + int mask; + int seg; + int aval; + + if (pcmVal >= 0) { + mask = 0xD5; // sign (7th) bit = 1 + } else { + mask = 0x55; // sign bit = 0 + pcmVal = -pcmVal - 1; + if (pcmVal < 0) pcmVal = 32767; + } + + seg = _search(pcmVal, _segEnd, 8); + + if (seg >= 8) return 0x7F ^ mask; + + aval = seg << SEG_SHIFT; + if (seg < 2) { + aval |= (pcmVal >> 4) & QUANT_MASK; + } else { + aval |= (pcmVal >> (seg + 3)) & QUANT_MASK; + } + return aval ^ mask; + } + + /** + * 将A-law编码值转换为线性PCM值 + * + * 转换步骤: + * 1. 异或操作恢复原始位模式 + * 2. 提取量化值和段号 + * 3. 根据段号计算线性值 + * 4. 应用符号位 + * + * @param aVal 8位A-law编码值 + * @return 16位线性PCM值 + */ + static int _alaw2linear(int aVal) { + int t; + int seg; + + aVal ^= 0x55; + + t = (aVal & QUANT_MASK) << 4; + seg = (aVal & SEG_MASK) >> SEG_SHIFT; + + switch (seg) { + case 0: + t += 8; + break; + case 1: + t += 0x108; + break; + default: + t += 0x108; + t <<= seg - 1; + } + return (aVal & SIGN_BIT) != 0 ? t : -t; + } + + /** + * 将线性PCM值转换为μ-law编码值 + * + * 转换步骤与A-law类似,但使用不同的偏置和掩码 + * + * @param pcmVal 16位线性PCM值 + * @return 8位μ-law编码值 + */ + static int _linear2ulaw(int pcmVal) { + int mask; + int seg; + int uval; + + if (pcmVal < 0) { + pcmVal = BIAS - pcmVal; + mask = 0x7F; + } else { + pcmVal += BIAS; + mask = 0xFF; + } + + seg = _search(pcmVal, _segEnd, 8); + + if (seg >= 8) return 0x7F ^ mask; + + uval = (seg << 4) | ((pcmVal >> (seg + 3)) & 0xF); + return uval ^ mask; + } + + /** + * 将μ-law编码值转换为线性PCM值 + * + * @param uVal 8位μ-law编码值 + * @return 16位线性PCM值 + */ + static int _ulaw2linear(int uVal) { + uVal = ~uVal; + int t = ((uVal & QUANT_MASK) << 3) + BIAS; + t <<= (uVal & SEG_MASK) >> SEG_SHIFT; + return (uVal & SIGN_BIT) != 0 ? (BIAS - t) : (t - BIAS); + } + + /** + * 将A-law编码值转换为μ-law编码值 + * + * 使用查找表直接转换,避免解码再编码的性能损失 + * + * @param aval 8位A-law编码值 + * @return 8位μ-law编码值 + */ + static int alaw2ulaw(int aval) { + aval &= 0xff; + return (aval & 0x80) != 0 + ? (0xFF ^ _a2u[aval ^ 0xD5]) + : (0x7F ^ _a2u[aval ^ 0x55]); + } + + /** + * 将μ-law编码值转换为A-law编码值 + * + * 使用查找表直接转换,避免解码再编码的性能损失 + * + * @param uval 8位μ-law编码值 + * @return 8位A-law编码值 + */ + static int ulaw2alaw(int uval) { + uval &= 0xff; + return (uval & 0x80) != 0 + ? (0xD5 ^ (_u2a[0xFF ^ uval] - 1)) + : (0x55 ^ (_u2a[0x7F ^ uval] - 1)); + } + + /** + * 将PCM数据编码为G.711格式 + * + * @param pcm 输入PCM数据 + * @param lawFlag 编码类型标志: 0表示A-law, 1表示μ-law + * @return 编码后的G.711数据 + */ + static List encode(List pcm, int lawFlag) { + List code = List.filled(pcm.length, 0); + + if (lawFlag == 0) { + for (int i = 0; i < pcm.length; i++) { + code[i] = _linear2alaw(pcm[i]); + } + } else { + for (int i = 0; i < pcm.length; i++) { + code[i] = _linear2ulaw(pcm[i]); + } + } + return code; + } + + /** + * 将G.711数据解码为PCM格式 + * + * @param code 输入G.711数据 + * @param lawFlag 编码类型标志: 0表示A-law, 1表示μ-law + * @return 解码后的PCM数据 + */ + static List decode(List code, int lawFlag) { + List pcm = List.filled(code.length, 0); + + if (lawFlag == 0) { + for (int i = 0; i < code.length; i++) { + pcm[i] = _alaw2linear(code[i]); + } + } else { + for (int i = 0; i < code.length; i++) { + pcm[i] = _ulaw2linear(code[i]); + } + } + return pcm; + } +}