import 'package:audioplayers/audioplayers.dart'; import 'package:flutter/foundation.dart'; abstract interface class ExerciseStepAudioCuePlayer { Future playShortCountdownBeep(); Future playLongCompletionBeep(); Future dispose(); } final class AudioplayersExerciseStepAudioCuePlayer implements ExerciseStepAudioCuePlayer { AudioplayersExerciseStepAudioCuePlayer(); final AudioPlayer _player = AudioPlayer(); late final Future _audioContextReady = _configureAudioContext(); @override Future playShortCountdownBeep() { return _play('audio/step_countdown_short.wav'); } @override Future playLongCompletionBeep() { return _play('audio/step_completion_long.wav'); } Future _play(String assetPath) async { await _audioContextReady; await _player.stop(); await _player.play(AssetSource(assetPath)); } Future _configureAudioContext() async { try { await _player.setAudioContext(_stepCueAudioContext); await _player.setPlayerMode(PlayerMode.lowLatency); await _player.setReleaseMode(ReleaseMode.stop); await _player.setVolume(1); } on Object catch (error) { debugPrint('Audio cue context ignored: $error'); } } @override Future dispose() { return _player.dispose(); } } final _stepCueAudioContext = AudioContext( android: const AudioContextAndroid( contentType: AndroidContentType.sonification, usageType: AndroidUsageType.assistanceSonification, audioFocus: AndroidAudioFocus.none, ), iOS: AudioContextIOS( category: AVAudioSessionCategory.playback, options: const {AVAudioSessionOptions.mixWithOthers}, ), ); final class NoOpExerciseStepAudioCuePlayer implements ExerciseStepAudioCuePlayer { const NoOpExerciseStepAudioCuePlayer(); @override Future playShortCountdownBeep() async {} @override Future playLongCompletionBeep() async {} @override Future dispose() async {} }