You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
154 lines
5.3 KiB
154 lines
5.3 KiB
import 'package:audioplayers/audioplayers.dart';
|
|
import 'package:get/get.dart';
|
|
|
|
/// 语音播放管理器,单例模式,统一管理语音播放
|
|
class VoicePlayerManager extends GetxController {
|
|
static VoicePlayerManager? _instance;
|
|
static VoicePlayerManager get instance {
|
|
_instance ??= Get.put(VoicePlayerManager());
|
|
return _instance!;
|
|
}
|
|
|
|
final AudioPlayer _audioPlayer = AudioPlayer();
|
|
String? _currentPlayingId;
|
|
final Rx<String?> currentPlayingId = Rx<String?>(null);
|
|
final Rx<PlayerState> playerState = Rx<PlayerState>(PlayerState.stopped);
|
|
|
|
VoicePlayerManager() {
|
|
// 监听播放状态变化
|
|
_audioPlayer.onPlayerStateChanged.listen((state) {
|
|
playerState.value = state;
|
|
if (state == PlayerState.completed) {
|
|
// 播放完成,重置状态
|
|
_currentPlayingId = null;
|
|
currentPlayingId.value = null;
|
|
}
|
|
});
|
|
}
|
|
|
|
/// 播放音频
|
|
/// [audioId] 音频的唯一标识(通常是消息ID)
|
|
/// [filePath] 音频文件路径
|
|
Future<void> play(String audioId, String filePath) async {
|
|
try {
|
|
print('🎵 [VoicePlayerManager] 准备播放音频: audioId=$audioId, filePath=$filePath');
|
|
|
|
// 如果是同一个音频,则暂停/恢复
|
|
if (_currentPlayingId == audioId) {
|
|
print('⏯️ [VoicePlayerManager] 切换播放/暂停状态');
|
|
try {
|
|
if (playerState.value == PlayerState.playing) {
|
|
await _audioPlayer.pause();
|
|
} else {
|
|
await _audioPlayer.resume();
|
|
}
|
|
} catch (e) {
|
|
print('⚠️ [VoicePlayerManager] 暂停/恢复失败,尝试重新播放: $e');
|
|
// 如果暂停/恢复失败,继续执行播放新音频的逻辑
|
|
}
|
|
// 如果成功暂停/恢复,直接返回
|
|
if (_currentPlayingId == audioId &&
|
|
(playerState.value == PlayerState.playing ||
|
|
playerState.value == PlayerState.paused)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// 如果正在播放其他音频,先停止
|
|
if (_currentPlayingId != null && _currentPlayingId != audioId) {
|
|
print('🛑 [VoicePlayerManager] 停止当前播放的音频: $_currentPlayingId');
|
|
try {
|
|
// 只在播放器状态不是 stopped 时才调用 stop
|
|
if (playerState.value != PlayerState.stopped) {
|
|
await _audioPlayer.stop();
|
|
}
|
|
} catch (e) {
|
|
print('⚠️ [VoicePlayerManager] 停止播放器时出错(可忽略): $e');
|
|
}
|
|
// 重置当前播放ID
|
|
_currentPlayingId = null;
|
|
currentPlayingId.value = null;
|
|
// 等待一小段时间确保停止完成
|
|
await Future.delayed(Duration(milliseconds: 200));
|
|
}
|
|
|
|
// 播放新音频前,如果播放器正在播放或暂停,先停止
|
|
print('🔄 [VoicePlayerManager] 准备播放新音频,当前状态: ${playerState.value}');
|
|
if (playerState.value == PlayerState.playing || playerState.value == PlayerState.paused) {
|
|
try {
|
|
await _audioPlayer.stop();
|
|
await Future.delayed(Duration(milliseconds: 150));
|
|
} catch (e) {
|
|
// 如果 stop 失败,可能是播放器还没有初始化,继续尝试播放
|
|
print('⚠️ [VoicePlayerManager] 停止播放器时出错(可忽略): $e');
|
|
}
|
|
}
|
|
|
|
// 设置当前播放的音频ID
|
|
_currentPlayingId = audioId;
|
|
currentPlayingId.value = audioId;
|
|
|
|
// 根据路径类型选择不同的播放源
|
|
if (filePath.startsWith('http://') || filePath.startsWith('https://')) {
|
|
print('🌐 [VoicePlayerManager] 使用网络URL播放: $filePath');
|
|
await _audioPlayer.play(UrlSource(filePath));
|
|
} else {
|
|
print('📁 [VoicePlayerManager] 使用本地文件播放: $filePath');
|
|
await _audioPlayer.play(DeviceFileSource(filePath));
|
|
}
|
|
|
|
print('✅ [VoicePlayerManager] 音频播放请求已发送');
|
|
} catch (e, stackTrace) {
|
|
print('❌ [VoicePlayerManager] 播放音频失败: $e');
|
|
print('📚 [VoicePlayerManager] 堆栈跟踪: $stackTrace');
|
|
// 重置状态
|
|
_currentPlayingId = null;
|
|
currentPlayingId.value = null;
|
|
// 重新抛出异常,让调用者知道播放失败
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
/// 停止播放
|
|
Future<void> stop() async {
|
|
try {
|
|
// 只在播放器状态不是 stopped 时才调用 stop
|
|
if (playerState.value != PlayerState.stopped) {
|
|
await _audioPlayer.stop();
|
|
}
|
|
_currentPlayingId = null;
|
|
currentPlayingId.value = null;
|
|
} catch (e) {
|
|
print('⚠️ [VoicePlayerManager] 停止播放失败(可忽略): $e');
|
|
// 即使停止失败,也重置状态
|
|
_currentPlayingId = null;
|
|
currentPlayingId.value = null;
|
|
}
|
|
}
|
|
|
|
/// 暂停播放
|
|
Future<void> pause() async {
|
|
try {
|
|
await _audioPlayer.pause();
|
|
} catch (e) {
|
|
print('暂停播放失败: $e');
|
|
}
|
|
}
|
|
|
|
/// 检查指定音频是否正在播放
|
|
bool isPlaying(String audioId) {
|
|
return _currentPlayingId == audioId &&
|
|
playerState.value == PlayerState.playing;
|
|
}
|
|
|
|
/// 检查指定音频是否已加载
|
|
bool isLoaded(String audioId) {
|
|
return _currentPlayingId == audioId;
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
_audioPlayer.dispose();
|
|
super.onClose();
|
|
}
|
|
}
|