// Author: fengshengxiong // Date: 2024/5/30 // Description: 播放器控制器 import 'dart:async'; import 'dart:io'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:just_audio/just_audio.dart'; import 'package:tone_snap/components/base_easyloading.dart'; import 'package:tone_snap/utils/audio_util.dart'; import 'package:tone_snap/utils/log_print.dart'; class PlayerController extends GetxController { static PlayerController get to => Get.put(PlayerController()); StreamSubscription? _playerStateSubscription; StreamSubscription? _durationSubscription; final _player = AudioPlayer(); var filePath = ''; var isPlaying = false.obs; var isCompleted = true.obs; var positionValue = 0.0.obs; var duration = const Duration().obs; var positionDuration = const Duration().obs; var isReady = false; @override void onInit() { super.onInit(); AudioUtil.configAudioSession(); } @override void onClose() { _stopListening(); _player.dispose(); super.onClose(); } Future setFilePath(String filePath) async { if (!filePath.contains('assets') && !await _fileExists(filePath)) return; if (this.filePath != filePath) { this.filePath = filePath; try { isReady = false; if (filePath.contains('assets')) { duration.value = await _player.setAsset(filePath) ?? Duration.zero; } else { duration.value = await _player.setFilePath(filePath) ?? Duration.zero; } isReady = true; } on PlayerException catch (e) { LogPrint.e("Error code: ${e.code}"); LogPrint.e("Error message: ${e.message}"); BaseEasyLoading.toast("Error message: ${e.message}"); } on PlayerInterruptedException catch (e) { LogPrint.e("Connection aborted: ${e.message}"); BaseEasyLoading.toast("Connection aborted: ${e.message}"); } catch (e) { LogPrint.e('An error occured: $e'); BaseEasyLoading.toast('An error occured: $e'); } } } /// 监听 void _startListening() { _playerStateSubscription = _player.playerStateStream.listen((playerState) { switch (playerState.processingState) { case ProcessingState.idle: break; case ProcessingState.loading: break; case ProcessingState.buffering: break; case ProcessingState.ready: break; case ProcessingState.completed: isCompleted.value = true; _player.seek(Duration.zero); pausePlay(); break; } }); _durationSubscription = _player.positionStream.listen((position) { if (_player.duration != null) { duration.value = _player.duration!; positionDuration.value = position; positionValue.value = position.inMilliseconds.toDouble() / _player.duration!.inMilliseconds.toDouble(); } }, onError: (Object e, StackTrace st) { if (e is PlatformException) { LogPrint.e('Error code: ${e.code}'); LogPrint.e('Error message: ${e.message}'); LogPrint.e('AudioSource index: ${e.details?['index']}'); BaseEasyLoading.toast('Error message: ${e.message}'); } else { LogPrint.e('An error occurred: $e'); BaseEasyLoading.toast('An error occurred: $e'); } }); } void _stopListening() { _playerStateSubscription?.cancel(); _playerStateSubscription = null; _durationSubscription?.cancel(); _durationSubscription = null; } /// 开始播放 Future startPlay() async { if (!filePath.contains('assets') && !await _fileExists(filePath)) return; if (!isReady) { BaseEasyLoading.toast('Loading audio source'); return; } _startListening(); _player.play(); isPlaying.value = true; isCompleted.value = false; } /// 暂停播放 Future pausePlay() async { await _player.pause(); isPlaying.value = false; _stopListening(); } /// 停止播放并释放资源 Future stopPlay() async { await _player.stop(); _stopListening(); _player.seek(Duration.zero); isPlaying.value = false; isCompleted.value = true; positionValue.value = 0.0; // duration.value = Duration.zero; positionDuration.value = Duration.zero; } String getDuration() { return printDuration(duration.value); } String getPositionDuration() { return printDuration(positionDuration.value); } /// 打印持续时间 String printDuration(Duration duration) { String twoDigits(int n) => n.toString().padLeft(2, '0'); final minutes = twoDigits(duration.inMinutes.remainder(60)); final seconds = twoDigits(duration.inSeconds.remainder(60)); return '$minutes:$seconds'; } /// 判断文件是否存在 Future _fileExists(String path) async { if (await File(path).exists()) { return true; } else { BaseEasyLoading.toast('Audio does not exist'); return false; } } /// 设置声调 Future setPitch(double setPitch) async { await _player.setPitch(setPitch); } /// 设置声速 Future setSpeed(double speed) async { await _player.setSpeed(speed); } }