|
|
@ -2,6 +2,7 @@ |
|
|
import 'dart:async'; |
|
|
import 'dart:async'; |
|
|
import 'dart:io'; |
|
|
import 'dart:io'; |
|
|
import 'dart:math'; |
|
|
import 'dart:math'; |
|
|
|
|
|
import 'dart:typed_data'; |
|
|
import 'package:deep_voice/core/utils/logger.dart'; |
|
|
import 'package:deep_voice/core/utils/logger.dart'; |
|
|
import 'package:deep_voice/core/utils/permission_util.dart'; |
|
|
import 'package:deep_voice/core/utils/permission_util.dart'; |
|
|
import 'package:flutter/material.dart'; |
|
|
import 'package:flutter/material.dart'; |
|
|
@ -56,10 +57,20 @@ class MeetingRecordController extends GetxController |
|
|
Timer? _timer; |
|
|
Timer? _timer; |
|
|
int _elapsedMilliseconds = 0; |
|
|
int _elapsedMilliseconds = 0; |
|
|
int? _lastStartTime; |
|
|
int? _lastStartTime; |
|
|
int _currentPosition = 0; |
|
|
|
|
|
bool _audioSourceType = false; |
|
|
bool _audioSourceType = false; |
|
|
late TabController tabController; |
|
|
late TabController tabController; |
|
|
|
|
|
|
|
|
|
|
|
// 添加波形放大倍数变量 |
|
|
|
|
|
final double waveAmplifyFactor = 100.0; // 增加波动幅度 |
|
|
|
|
|
final RxDouble ursorPosition = 0.0.obs; // 当前播放位置 |
|
|
|
|
|
final RxInt _currentPosition = 0.obs; // 原始位置索引 |
|
|
|
|
|
int maxPoints = 500; // 最大显示点数(根据屏幕宽度优化) |
|
|
|
|
|
int sampleRate = 16000; // 假设音频采样率(根据实际情况调整) |
|
|
|
|
|
// 设置最大点数(根据屏幕宽度) |
|
|
|
|
|
void setMaxPoints(double screenWidth) { |
|
|
|
|
|
maxPoints = screenWidth ~/ 2; // 每2像素一个数据点 |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
@override |
|
|
@override |
|
|
void onInit() { |
|
|
void onInit() { |
|
|
super.onInit(); |
|
|
super.onInit(); |
|
|
@ -291,6 +302,10 @@ class MeetingRecordController extends GetxController |
|
|
case RecognitionEventType.intermediateResult: |
|
|
case RecognitionEventType.intermediateResult: |
|
|
intermediateContent.value = event.text; |
|
|
intermediateContent.value = event.text; |
|
|
break; |
|
|
break; |
|
|
|
|
|
case RecognitionEventType.onAudio: |
|
|
|
|
|
// 添加新音频数据到波形 |
|
|
|
|
|
_processAudioData(event.audio!); |
|
|
|
|
|
break; |
|
|
case RecognitionEventType.finalResult: |
|
|
case RecognitionEventType.finalResult: |
|
|
finalContent.value += _formatTranscript(event); |
|
|
finalContent.value += _formatTranscript(event); |
|
|
intermediateContent.value = ''; |
|
|
intermediateContent.value = ''; |
|
|
@ -300,6 +315,79 @@ class MeetingRecordController extends GetxController |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// 处理原始Uint8List音频数据 |
|
|
|
|
|
void _processAudioData(Uint8List audioData) { |
|
|
|
|
|
// 将Uint8List转换为Float32List(PCM16 -> 浮点) |
|
|
|
|
|
final pcmData = _convertToPCM(audioData); |
|
|
|
|
|
|
|
|
|
|
|
// 降采样和添加数据 |
|
|
|
|
|
_addAudioData(pcmData); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// 转换Uint8List为PCM浮点数组 |
|
|
|
|
|
Float32List _convertToPCM(Uint8List uint8List) { |
|
|
|
|
|
// 假设音频为16位PCM小端格式 |
|
|
|
|
|
final byteData = ByteData.sublistView(uint8List); |
|
|
|
|
|
final length = uint8List.length ~/ 2; |
|
|
|
|
|
final pcmData = Float32List(length); |
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < length; i++) { |
|
|
|
|
|
// 读取16位有符号整数 (-32768 到 32767) |
|
|
|
|
|
final int intValue = byteData.getInt16(i * 2, Endian.little); |
|
|
|
|
|
// 转换为浮点数 (-1.0 到 1.0) |
|
|
|
|
|
pcmData[i] = intValue / 32768.0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return pcmData; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// 降采样音频数据 |
|
|
|
|
|
List<double> _downsampleAudio(Float32List audio) { |
|
|
|
|
|
if (audio.isEmpty) return []; |
|
|
|
|
|
|
|
|
|
|
|
final result = <double>[]; |
|
|
|
|
|
final step = (audio.length / maxPoints).ceil(); |
|
|
|
|
|
|
|
|
|
|
|
// 计算每个区间的峰值(保留更多细节) |
|
|
|
|
|
for (int i = 0; i < audio.length; i += step) { |
|
|
|
|
|
double maxVal = 0.0; |
|
|
|
|
|
final end = min(i + step, audio.length); |
|
|
|
|
|
|
|
|
|
|
|
for (int j = i; j < end; j++) { |
|
|
|
|
|
final absVal = audio[j].abs(); |
|
|
|
|
|
if (absVal > maxVal) maxVal = absVal; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 应用放大倍数增强波动效果 |
|
|
|
|
|
result.add(maxVal * waveAmplifyFactor); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 获取当前光标位置(0.0 - 1.0) |
|
|
|
|
|
double get cursorPositionValue { |
|
|
|
|
|
if (waveData.isEmpty) return 0.0; |
|
|
|
|
|
return _currentPosition.value / waveData.length; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// 添加处理后的音频数据 |
|
|
|
|
|
void _addAudioData(Float32List newAudio) { |
|
|
|
|
|
// 1. 降采样:只保留部分数据点 |
|
|
|
|
|
final downsampled = _downsampleAudio(newAudio); |
|
|
|
|
|
|
|
|
|
|
|
waveData.addAll(downsampled); |
|
|
|
|
|
|
|
|
|
|
|
// 3. 更新光标位置到最新点 |
|
|
|
|
|
_currentPosition.value = waveData.length; |
|
|
|
|
|
|
|
|
|
|
|
// 4. 限制数据长度,移除旧数据 |
|
|
|
|
|
if (waveData.length > maxPoints * 2) { |
|
|
|
|
|
waveData.removeRange(0, waveData.length - maxPoints); |
|
|
|
|
|
_currentPosition.value = waveData.length; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/// Formats transcript with speaker identification |
|
|
/// Formats transcript with speaker identification |
|
|
String _formatTranscript(RecognitionEvent event) { |
|
|
String _formatTranscript(RecognitionEvent event) { |
|
|
final buffer = StringBuffer(); |
|
|
final buffer = StringBuffer(); |
|
|
|