import 'dart:convert'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/enum/audio_encoding_enum.dart'; import 'package:fluffychat/pangea/models/pangea_token_model.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; const int THRESHOLD_FOR_GREEN = 80; class SpeechToTextAudioConfigModel { final AudioEncodingEnum encoding; final int sampleRateHertz; final bool enableWordConfidence; final bool enableAutomaticPunctuation; final String userL1; final String userL2; SpeechToTextAudioConfigModel({ required this.encoding, required this.userL1, required this.userL2, this.sampleRateHertz = 16000, this.enableWordConfidence = true, this.enableAutomaticPunctuation = true, }); Map toJson() => { "encoding": encoding.value, "sample_rate_hertz": sampleRateHertz, "user_l1": userL1, "user_l2": userL2, "enable_word_confidence": enableWordConfidence, "enable_automatic_punctuation": enableAutomaticPunctuation, }; } class SpeechToTextRequestModel { final Uint8List audioContent; final SpeechToTextAudioConfigModel config; final Event? audioEvent; SpeechToTextRequestModel({ required this.audioContent, required this.config, this.audioEvent, }); Map toJson() => { "audio_content": base64Encode(audioContent), "config": config.toJson(), }; @override bool operator ==(Object other) { if (identical(this, other)) return true; if (other is! SpeechToTextRequestModel) return false; return listEquals(audioContent, other.audioContent) && config == other.config; } @override int get hashCode { final bytesSample = audioContent.length > 10 ? audioContent.sublist(0, 10) : audioContent; return Object.hashAll([ Object.hashAll(bytesSample), config.hashCode, ]); } } class STTToken { final PangeaToken token; final Duration? startTime; final Duration? endTime; final int? confidence; STTToken({ required this.token, this.startTime, this.endTime, this.confidence, }); int get offset => token.text.offset; int get length => token.text.length; Color color(BuildContext context) { if (confidence == null) { return Theme.of(context).textTheme.bodyMedium?.color ?? (Theme.of(context).brightness == Brightness.dark ? Colors.white : Colors.black); } if (confidence! > THRESHOLD_FOR_GREEN) { return AppConfig.success; } return AppConfig.warning; } factory STTToken.fromJson(Map json) { // debugPrint('STTToken.fromJson: $json'); return STTToken( token: PangeaToken.fromJson(json['token']), startTime: json['start_time'] != null ? Duration(milliseconds: (json['start_time'] * 1000).round()) : null, endTime: json['end_time'] != null ? Duration(milliseconds: (json['end_time'] * 1000).round()) : null, confidence: json['confidence'], ); } Map toJson() => { "token": token.toJson(), "start_time": startTime?.inMilliseconds, "end_time": endTime?.inMilliseconds, "confidence": confidence, }; @override bool operator ==(Object other) { if (identical(this, other)) return true; if (other is! STTToken) return false; return token == other.token && startTime == other.startTime && endTime == other.endTime && confidence == other.confidence; } @override int get hashCode { return Object.hashAll([ token.hashCode, startTime.hashCode, endTime.hashCode, confidence.hashCode, ]); } } class Transcript { final String text; final int confidence; final int wordsPerMinute; final List sttTokens; final String langCode; Transcript({ required this.text, required this.confidence, required this.sttTokens, required this.langCode, required this.wordsPerMinute, }); factory Transcript.fromJson(Map json) => Transcript( text: json['transcript'], confidence: json['confidence'] <= 100 ? json['confidence'] : json['confidence'] / 100, sttTokens: (json['stt_tokens'] as List) .map((e) => STTToken.fromJson(e)) .toList(), langCode: json['lang_code'], wordsPerMinute: json['words_per_minute'].round(), ); Map toJson() => { "transcript": text, "confidence": confidence, "stt_tokens": sttTokens.map((e) => e.toJson()).toList(), "lang_code": langCode, "words_per_minute": wordsPerMinute, }; Color color(BuildContext context) { if (confidence > THRESHOLD_FOR_GREEN) { return AppConfig.success; } return AppConfig.warning; } } class SpeechToTextResult { final List transcripts; SpeechToTextResult({required this.transcripts}); factory SpeechToTextResult.fromJson(Map json) => SpeechToTextResult( transcripts: (json['transcripts'] as List) .map((e) => Transcript.fromJson(e)) .toList(), ); Map toJson() => { "transcripts": transcripts.map((e) => e.toJson()).toList(), }; } class SpeechToTextModel { final List results; SpeechToTextModel({ required this.results, }); Transcript get transcript => results.first.transcripts.first; String get langCode => results.first.transcripts.first.langCode; factory SpeechToTextModel.fromJson(Map json) => SpeechToTextModel( results: (json['results'] as List) .map((e) => SpeechToTextResult.fromJson(e)) .toList(), ); Map toJson() => { "results": results.map((e) => e.toJson()).toList(), }; }