diff --git a/lib/talk/startChart/command/message_command.dart b/lib/talk/startChart/command/message_command.dart index a9018fbe..c90a207a 100644 --- a/lib/talk/startChart/command/message_command.dart +++ b/lib/talk/startChart/command/message_command.dart @@ -11,6 +11,7 @@ import 'package:star_lock/talk/startChart/proto/gateway_reset.pb.dart'; import 'package:star_lock/talk/startChart/proto/generic.pb.dart'; import 'package:star_lock/talk/startChart/proto/rbcu.pb.dart'; import 'package:star_lock/talk/startChart/proto/talk_accept.pb.dart'; +import 'package:star_lock/talk/startChart/proto/talk_data.pb.dart'; import 'package:star_lock/talk/startChart/proto/talk_expect.pb.dart'; import 'package:star_lock/talk/startChart/proto/talk_hangup.pb.dart'; import 'package:star_lock/talk/startChart/proto/talk_ping.pb.dart'; @@ -272,16 +273,12 @@ class MessageCommand { required String FromPeerId, required String ToPeerId, int? MessageId, - List? payload, + required TalkData talkData, int? SpTotal, int? SpIndex, }) { - // 将 payload 中的每个整数转换为 4 字节序列 - // final payloadBytes = ByteData(payload!.length * 4); - // for (int i = 0; i < payload.length; i++) { - // payloadBytes.setInt32(i * 4, payload[i], Endian.big); // 使用大端序 - // } - // final payload = talkData.writeToBuffer(); + + final payload = talkData.writeToBuffer(); ScpMessage message = ScpMessage( ProtocolFlag: ProtocolFlagConstant.scp01, MessageType: MessageTypeConstant.RealTimeData, @@ -291,7 +288,7 @@ class MessageCommand { FromPeerId: FromPeerId, ToPeerId: ToPeerId, Payload: payload, - PayloadCRC: calculationCrcFromIntList(payload!), + PayloadCRC: calculationCrcFromIntList(payload), PayloadLength: payload.length, PayloadType: PayloadTypeConstant.talkData, ); diff --git a/lib/talk/startChart/start_chart_manage.dart b/lib/talk/startChart/start_chart_manage.dart index ce5fd29b..f29d052d 100644 --- a/lib/talk/startChart/start_chart_manage.dart +++ b/lib/talk/startChart/start_chart_manage.dart @@ -438,7 +438,7 @@ class StartChartManage { end = payload.length; } // 取到分包数据 - List packet = payload.sublist(start, end); + List packetTalkData = payload.sublist(start, end); // 分包数据不递增messageID final messageId = @@ -447,12 +447,15 @@ class StartChartManage { final message = MessageCommand.talkDataMessage( ToPeerId: toPeerId, FromPeerId: FromPeerId, - payload: packet, + talkData: TalkData( + contentType: talkData.contentType, + content: packetTalkData, + durationMs: talkData.durationMs, + ), SpTotal: totalPackets, SpIndex: i + 1, MessageId: messageId, ); - // 发送消息 await _sendMessage(message: message); } diff --git a/lib/talk/startChart/views/talkView/talk_view_logic.dart b/lib/talk/startChart/views/talkView/talk_view_logic.dart index d739f327..49bef73a 100644 --- a/lib/talk/startChart/views/talkView/talk_view_logic.dart +++ b/lib/talk/startChart/views/talkView/talk_view_logic.dart @@ -8,10 +8,8 @@ import 'package:flutter/services.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_pcm_sound/flutter_pcm_sound.dart'; import 'package:flutter_screen_recording/flutter_screen_recording.dart'; - import 'package:flutter_voice_processor/flutter_voice_processor.dart'; import 'package:gallery_saver/gallery_saver.dart'; - import 'package:get/get.dart'; import 'package:image_gallery_saver/image_gallery_saver.dart'; import 'package:path_provider/path_provider.dart'; @@ -560,6 +558,7 @@ class TalkViewLogic extends BaseGetXController { } on PlatformException catch (ex) { // state.errorMessage.value = 'Failed to start recorder: $ex'; } + state.isOpenVoice.value = false; } /// 停止录音 @@ -581,34 +580,20 @@ class TalkViewLogic extends BaseGetXController { } finally { final bool? isRecording = await state.voiceProcessor?.isRecording(); state.isRecordingAudio.value = isRecording!; + state.isOpenVoice.value = true; } } Future _onFrame(List frame) async { - // state.recordingAudioAllFrames.add(frame); // 将帧添加到状态变量中 - // final List concatenatedFrames = - // concatenateFrames(state.recordingAudioAllFrames); // 连接所有帧 - // final List pcmBytes = _listLinearToULaw(frame); - // final aLaw = G711().encodeALaw(frame); - // final aLawFrame = listLinearToALaw(frame); - // 创建 640 个 0 的 PCM 数据 - // 创建 640 个 0 的 8 位 PCM 数据(无符号) - final pcmSamples = List.filled(640, 0); // 128 是 8 位 PCM 的 0 值 - - // 编码为 A-law - final aLawSamples = listLinearToALaw(pcmSamples, isUnsigned: true); - - AppLog.log('msg'); - - // AppLog.log('录制的音频数据(A-law):$aLawFrame, size:${aLawFrame.length}'); - + final list = listLinearToALaw(frame); + final ms = DateTime.now().millisecondsSinceEpoch - + state.startRecordingAudioTime.value.millisecondsSinceEpoch; // 发送音频数据 await StartChartManage().sendTalkDataMessage( talkData: TalkData( - content: aLawSamples, + content: list, contentType: TalkData_ContentTypeE.G711, - durationMs: DateTime.now().millisecondsSinceEpoch - - state.startRecordingAudioTime.value.millisecondsSinceEpoch, + durationMs: ms, ), ); } @@ -618,77 +603,69 @@ class TalkViewLogic extends BaseGetXController { AppLog.log(error.message!); } - int linearToALaw(int pcmVal) { - const int ALAW_MAX = 0x7FFF; // 16 位 PCM 的最大值 - const int ALAW_BIAS = 0x84; // A-law 偏置值 + List listLinearToALaw(List pcmList) { + final List aLawList = []; + for (int pcmVal in pcmList) { + final int aLawVal = linearToALaw(pcmVal); + aLawList.add(aLawVal); + } + return aLawList; + } - // 处理符号位 - int sign = (pcmVal & 0x8000) != 0 ? 0x00 : 0x80; // A-law 符号位 - if (sign == 0x80) { - pcmVal = -pcmVal; // 取绝对值 + int linearToALaw(int pcmVal) { + const int ALAW_MAX = 0x7FFF; // 32767 + const int ALAW_BIAS = 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) } - // 限制 PCM 值在有效范围内 + // Add bias and clamp to ALAW_MAX + pcmVal += ALAW_BIAS; if (pcmVal > ALAW_MAX) { pcmVal = ALAW_MAX; } - // 添加偏置 - pcmVal += ALAW_BIAS; + // Determine segment + seg = search(pcmVal); - // 查找段和量化值 - int seg = searchALawSegment(pcmVal); - int quantizedValue = (pcmVal >> (seg + 3)) & 0x0F; + // Calculate A-law value + if (seg >= 8) { + aLawVal = 0x7F ^ mask; // Clamp to maximum value + } else { + int quantized = (pcmVal >> (seg + 3)) & 0xF; + aLawVal = (seg << 4) | quantized; + aLawVal ^= 0xD5; // XOR with 0xD5 to match standard A-law table + } - // 生成 A-law 编码 - int aLawVal = sign | (seg << 4) | quantizedValue; return aLawVal; } - int searchALawSegment(int val) { - const List ALAW_SEGMENT_TABLE = [ - 0x1F, - 0x3F, - 0x7F, - 0xFF, - 0x1FF, - 0x3FF, - 0x7FF, - 0xFFF + 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 <= ALAW_SEGMENT_TABLE[i]) { + if (val <= table[i]) { return i; } } return size; } - - List listLinearToALaw(List pcmList, {bool isUnsigned = true}) { - final List aLawList = []; - - // 每两个 8 位 PCM 数据组合成一个 16 位 PCM 数据 - for (int i = 0; i < pcmList.length; i += 2) { - int pcm8High = pcmList[i]; - int pcm8Low = (i + 1 < pcmList.length) ? pcmList[i + 1] : 0; // 如果不足,补 0 - - // 将两个 8 位 PCM 数据组合成一个 16 位 PCM 数据 - int pcm16; - if (isUnsigned) { - // 无符号 8 位 PCM 数据扩展为 16 位 PCM 数据 - pcm16 = ((pcm8High - 128) << 8) | (pcm8Low - 128); - } else { - // 有符号 8 位 PCM 数据扩展为 16 位 PCM 数据 - pcm16 = (pcm8High << 8) | pcm8Low; - } - - // 将 16 位 PCM 数据编码为 A-law - final int aLawVal = linearToALaw(pcm16); - aLawList.add(aLawVal); - } - - return aLawList; - } } diff --git a/lib/talk/startChart/views/talkView/talk_view_state.dart b/lib/talk/startChart/views/talkView/talk_view_state.dart index 33a97af9..8c1ffbd7 100644 --- a/lib/talk/startChart/views/talkView/talk_view_state.dart +++ b/lib/talk/startChart/views/talkView/talk_view_state.dart @@ -82,7 +82,7 @@ class TalkViewState { RxInt recordingAudioTime = 0.obs; // 录音时间持续时间 RxDouble fps = 0.0.obs; // 添加 FPS 计数 late VoiceProcessor? voiceProcessor; // 音频处理器、录音 - final int frameLength = 640; //录音视频帧长度为640 + final int frameLength = 320; //录音视频帧长度为640 final int sampleRate = 8000; //录音频采样率为8000 List> recordingAudioAllFrames = >[]; // 录制音频的所有帧 RxInt rotateAngle = 0.obs; // 旋转角度(以弧度为单位)