feat: While audio is playing, allow clicking of word to move audio to that spot
This commit is contained in:
parent
b8bb4ea4a0
commit
e9e4604aad
4 changed files with 38 additions and 18 deletions
|
|
@ -15,7 +15,6 @@ import 'package:path_provider/path_provider.dart';
|
|||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/message_practice/message_audio_card.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/message_selection_overlay.dart';
|
||||
import 'package:fluffychat/utils/error_reporter.dart';
|
||||
import 'package:fluffychat/utils/file_description.dart';
|
||||
import 'package:fluffychat/utils/localized_exception_extension.dart';
|
||||
|
|
@ -34,7 +33,6 @@ class AudioPlayerWidget extends StatefulWidget {
|
|||
final String roomId;
|
||||
final String senderId;
|
||||
final PangeaAudioFile? matrixFile;
|
||||
final MessageOverlayController? overlayController;
|
||||
final bool autoplay;
|
||||
// Pangea#
|
||||
|
||||
|
|
@ -50,7 +48,6 @@ class AudioPlayerWidget extends StatefulWidget {
|
|||
required this.roomId,
|
||||
required this.senderId,
|
||||
this.matrixFile,
|
||||
this.overlayController,
|
||||
this.autoplay = false,
|
||||
// Pangea#
|
||||
super.key,
|
||||
|
|
@ -72,7 +69,6 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
|||
String? _durationString;
|
||||
|
||||
// #Pangea
|
||||
StreamSubscription? _onAudioPositionChanged;
|
||||
StreamSubscription? _onAudioStateChanged;
|
||||
|
||||
double playbackSpeed = 1.0;
|
||||
|
|
@ -154,7 +150,6 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
|||
audioPlayer.dispose();
|
||||
matrix.voiceMessageEventId.value = matrix.audioPlayer = null;
|
||||
// #Pangea
|
||||
_onAudioPositionChanged?.cancel();
|
||||
_onAudioStateChanged?.cancel();
|
||||
// Pangea#
|
||||
}
|
||||
|
|
@ -262,18 +257,6 @@ class AudioPlayerState extends State<AudioPlayerWidget> {
|
|||
|
||||
// #Pangea
|
||||
audioPlayer.setSpeed(playbackSpeed);
|
||||
_onAudioPositionChanged?.cancel();
|
||||
_onAudioPositionChanged =
|
||||
matrix.audioPlayer!.positionStream.listen((state) {
|
||||
// Pass current timestamp to overlay, so it can highlight as necessary
|
||||
if (widget.matrixFile?.tokens != null) {
|
||||
widget.overlayController?.highlightCurrentText(
|
||||
state.inMilliseconds,
|
||||
widget.matrixFile!.tokens!,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
_onAudioStateChanged?.cancel();
|
||||
_onAudioStateChanged =
|
||||
matrix.audioPlayer!.playerStateStream.listen((state) {
|
||||
|
|
|
|||
|
|
@ -193,9 +193,15 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
|
|||
return;
|
||||
}
|
||||
|
||||
if (selectedSpan == _selectedSpan) return;
|
||||
if (selectedSpan == _selectedSpan) {
|
||||
selectModeController.setPlayingToken(selectedToken?.text);
|
||||
return;
|
||||
}
|
||||
|
||||
_selectedSpan = selectedSpan;
|
||||
selectedTokenNotifier.value = selectedToken;
|
||||
selectModeController.setPlayingToken(selectedToken?.text);
|
||||
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
if (selectedToken != null && isNewToken(selectedToken!)) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'dart:io';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:just_audio/just_audio.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
|
@ -156,6 +157,8 @@ class SelectModeButtonsState extends State<SelectModeButtons> {
|
|||
if (messageEvent.isAudioMessage == true) {
|
||||
controller.fetchTranscription();
|
||||
}
|
||||
|
||||
controller.playTokenNotifier.addListener(_playToken);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -165,6 +168,7 @@ class SelectModeButtonsState extends State<SelectModeButtons> {
|
|||
matrix?.voiceMessageEventId.value = null;
|
||||
_audioSub?.cancel();
|
||||
_playerStateSub?.cancel();
|
||||
controller.playTokenNotifier.removeListener(_playToken);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
@ -304,6 +308,26 @@ class SelectModeButtonsState extends State<SelectModeButtons> {
|
|||
}
|
||||
}
|
||||
|
||||
void _playToken() {
|
||||
final token = controller.playTokenNotifier.value;
|
||||
|
||||
if (token == null ||
|
||||
controller.audioFile?.$1.tokens == null ||
|
||||
controller.selectedMode.value != SelectMode.audio) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ttsToken = controller.audioFile!.$1.tokens!.firstWhereOrNull(
|
||||
(t) => t.text == token,
|
||||
);
|
||||
|
||||
if (ttsToken != null && matrix?.audioPlayer != null) {
|
||||
final start = Duration(milliseconds: ttsToken.startMS);
|
||||
matrix!.audioPlayer!.seek(start);
|
||||
matrix!.audioPlayer!.play();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import 'package:fluffychat/pangea/analytics_misc/lemma_emoji_setter_mixin.dart';
|
|||
import 'package:fluffychat/pangea/common/utils/async_state.dart';
|
||||
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
|
||||
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
|
||||
import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart';
|
||||
import 'package:fluffychat/pangea/speech_to_text/speech_to_text_response_model.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/message_practice/message_audio_card.dart';
|
||||
import 'package:fluffychat/pangea/toolbar/reading_assistance/select_mode_buttons.dart';
|
||||
|
|
@ -92,10 +93,13 @@ class SelectModeController with LemmaEmojiSetter {
|
|||
ValueNotifier<(ConstructIdentifier, String)?>(null);
|
||||
|
||||
final StreamController contentChangedStream = StreamController.broadcast();
|
||||
ValueNotifier<PangeaTokenText?> playTokenNotifier =
|
||||
ValueNotifier<PangeaTokenText?>(null);
|
||||
|
||||
void dispose() {
|
||||
selectedMode.dispose();
|
||||
constructEmojiNotifier.dispose();
|
||||
playTokenNotifier.dispose();
|
||||
_transcriptLoader.dispose();
|
||||
_translationLoader.dispose();
|
||||
_sttTranslationLoader.dispose();
|
||||
|
|
@ -197,6 +201,9 @@ class SelectModeController with LemmaEmojiSetter {
|
|||
) =>
|
||||
constructEmojiNotifier.value = (constructId, emoji);
|
||||
|
||||
void setPlayingToken(PangeaTokenText? token) =>
|
||||
playTokenNotifier.value = token;
|
||||
|
||||
Future<void> fetchAudio() => _audioLoader.load();
|
||||
Future<void> fetchTranslation() => _translationLoader.load();
|
||||
Future<void> fetchTranscription() => _transcriptLoader.load();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue