Files
ai_english/client/lib/features/speaking/services/speech_recognition_service.dart

316 lines
9.0 KiB
Dart
Raw Normal View History

2025-11-17 13:39:05 +08:00
import 'dart:async';
import 'dart:math';
import '../models/pronunciation_item.dart';
/// 语音识别和评分服务
class SpeechRecognitionService {
static final SpeechRecognitionService _instance = SpeechRecognitionService._internal();
factory SpeechRecognitionService() => _instance;
SpeechRecognitionService._internal();
bool _isListening = false;
StreamController<double>? _volumeController;
StreamController<String>? _recognitionController;
/// 获取音量流
Stream<double> get volumeStream => _volumeController?.stream ?? const Stream.empty();
/// 获取识别结果流
Stream<String> get recognitionStream => _recognitionController?.stream ?? const Stream.empty();
/// 是否正在监听
bool get isListening => _isListening;
/// 开始语音识别
Future<bool> startListening() async {
if (_isListening) return false;
try {
_isListening = true;
_volumeController = StreamController<double>.broadcast();
_recognitionController = StreamController<String>.broadcast();
// TODO: 实现真实的语音识别
// 这里应该调用语音识别API如Google Speech-to-Text、百度语音识别等
// 模拟音量变化
_simulateVolumeChanges();
return true;
} catch (e) {
_isListening = false;
return false;
}
}
/// 停止语音识别
Future<void> stopListening() async {
if (!_isListening) return;
_isListening = false;
await _volumeController?.close();
await _recognitionController?.close();
_volumeController = null;
_recognitionController = null;
}
/// 分析发音并评分
Future<PronunciationResult> analyzePronunciation(
String recordedText,
PronunciationItem targetItem,
) async {
// TODO: 实现真实的发音分析
// 这里应该调用发音评估API如科大讯飞语音评测、腾讯云语音评测等
// 模拟分析过程
await Future.delayed(const Duration(milliseconds: 1500));
return _simulateAnalysis(recordedText, targetItem);
}
/// 模拟音量变化
void _simulateVolumeChanges() {
Timer.periodic(const Duration(milliseconds: 100), (timer) {
if (!_isListening) {
timer.cancel();
return;
}
// 生成随机音量值
final volume = Random().nextDouble() * 0.8 + 0.1;
_volumeController?.add(volume);
});
}
/// 模拟发音分析
PronunciationResult _simulateAnalysis(String recordedText, PronunciationItem targetItem) {
final random = Random();
// 基础分数
double baseScore = 60.0 + random.nextDouble() * 30;
// 根据文本相似度调整分数
final similarity = _calculateSimilarity(recordedText.toLowerCase(), targetItem.text.toLowerCase());
final adjustedScore = baseScore + (similarity * 20);
// 确保分数在合理范围内
final finalScore = adjustedScore.clamp(0.0, 100.0);
return PronunciationResult(
score: finalScore,
accuracy: _getAccuracyLevel(finalScore),
feedback: _generateFeedback(finalScore, targetItem),
detailedAnalysis: _generateDetailedAnalysis(finalScore, targetItem),
suggestions: _generateSuggestions(finalScore, targetItem),
recognizedText: recordedText,
);
}
/// 计算文本相似度
double _calculateSimilarity(String text1, String text2) {
if (text1 == text2) return 1.0;
final words1 = text1.split(' ');
final words2 = text2.split(' ');
int matches = 0;
final maxLength = max(words1.length, words2.length);
for (int i = 0; i < min(words1.length, words2.length); i++) {
if (words1[i] == words2[i]) {
matches++;
}
}
return matches / maxLength;
}
/// 获取准确度等级
AccuracyLevel _getAccuracyLevel(double score) {
if (score >= 90) return AccuracyLevel.excellent;
if (score >= 80) return AccuracyLevel.good;
if (score >= 70) return AccuracyLevel.fair;
if (score >= 60) return AccuracyLevel.needsImprovement;
return AccuracyLevel.poor;
}
/// 生成反馈
String _generateFeedback(double score, PronunciationItem item) {
if (score >= 90) {
return '发音非常标准!语调和节奏都很自然。';
} else if (score >= 80) {
return '发音很好,只需要在个别音素上稍作调整。';
} else if (score >= 70) {
return '发音基本正确,建议多练习重音和语调。';
} else if (score >= 60) {
return '发音需要改进,请注意音素的准确性。';
} else {
return '发音需要大幅改进,建议从基础音素开始练习。';
}
}
/// 生成详细分析
Map<String, double> _generateDetailedAnalysis(double score, PronunciationItem item) {
final random = Random();
final baseVariation = (score - 70) / 30; // 基于总分的变化
return {
'音素准确度': (70 + baseVariation * 20 + random.nextDouble() * 10).clamp(0, 100),
'语调自然度': (65 + baseVariation * 25 + random.nextDouble() * 15).clamp(0, 100),
'语速适中度': (75 + baseVariation * 15 + random.nextDouble() * 10).clamp(0, 100),
'重音正确度': (70 + baseVariation * 20 + random.nextDouble() * 10).clamp(0, 100),
'流畅度': (60 + baseVariation * 30 + random.nextDouble() * 15).clamp(0, 100),
};
}
/// 生成改进建议
List<String> _generateSuggestions(double score, PronunciationItem item) {
List<String> suggestions = [];
if (score < 80) {
suggestions.addAll([
'多听标准发音,模仿语调变化',
'注意重音位置,突出重读音节',
]);
}
if (score < 70) {
suggestions.addAll([
'放慢语速,确保每个音素清晰',
'练习困难音素的发音方法',
]);
}
if (score < 60) {
suggestions.addAll([
'从基础音标开始系统学习',
'使用镜子观察口型变化',
]);
}
// 根据练习类型添加特定建议
switch (item.type) {
case PronunciationType.word:
suggestions.add('分解单词,逐个音节练习');
break;
case PronunciationType.sentence:
suggestions.add('注意句子的语调起伏和停顿');
break;
case PronunciationType.phrase:
suggestions.add('练习短语内部的连读现象');
break;
case PronunciationType.phoneme:
suggestions.add('重点练习该音素的舌位和口型');
break;
}
return suggestions;
}
/// 获取发音提示
List<String> getPronunciationTips(PronunciationItem item) {
return item.tips;
}
/// 播放标准发音
Future<void> playStandardAudio(String audioUrl) async {
// TODO: 实现音频播放功能
// 这里应该使用音频播放插件如audioplayers
// 模拟播放时间
await Future.delayed(const Duration(seconds: 2));
}
/// 检查麦克风权限
Future<bool> checkMicrophonePermission() async {
// TODO: 实现权限检查
// 这里应该使用权限插件如permission_handler
// 模拟权限检查
return true;
}
/// 请求麦克风权限
Future<bool> requestMicrophonePermission() async {
// TODO: 实现权限请求
// 这里应该使用权限插件如permission_handler
// 模拟权限请求
return true;
}
}
/// 发音分析结果
class PronunciationResult {
final double score;
final AccuracyLevel accuracy;
final String feedback;
final Map<String, double> detailedAnalysis;
final List<String> suggestions;
final String recognizedText;
PronunciationResult({
required this.score,
required this.accuracy,
required this.feedback,
required this.detailedAnalysis,
required this.suggestions,
required this.recognizedText,
});
}
/// 准确度等级
enum AccuracyLevel {
excellent,
good,
fair,
needsImprovement,
poor,
}
extension AccuracyLevelExtension on AccuracyLevel {
String get displayName {
switch (this) {
case AccuracyLevel.excellent:
return '优秀';
case AccuracyLevel.good:
return '良好';
case AccuracyLevel.fair:
return '一般';
case AccuracyLevel.needsImprovement:
return '需要改进';
case AccuracyLevel.poor:
return '较差';
}
}
String get description {
switch (this) {
case AccuracyLevel.excellent:
return '发音非常标准,语调自然';
case AccuracyLevel.good:
return '发音准确,略有改进空间';
case AccuracyLevel.fair:
return '发音基本正确,需要练习';
case AccuracyLevel.needsImprovement:
return '发音有明显问题,需要改进';
case AccuracyLevel.poor:
return '发音问题较多,需要系统练习';
}
}
String get emoji {
switch (this) {
case AccuracyLevel.excellent:
return '🌟';
case AccuracyLevel.good:
return '👍';
case AccuracyLevel.fair:
return '👌';
case AccuracyLevel.needsImprovement:
return '📈';
case AccuracyLevel.poor:
return '💪';
}
}
}