import 'package:flutter/services.dart'; import 'package:flame_audio/flame_audio.dart'; import 'dart:math'; import 'dart:io' show Platform; class AudioManager { static final AudioManager _instance = AudioManager._internal(); factory AudioManager() => _instance; AudioManager._internal(); final Random _random = Random(); bool _soundEnabled = true; bool _musicEnabled = true; bool _hapticEnabled = true; double _bgmVolume = 0.4; // 40% default double _sfxVolume = 0.6; // 60% default bool _isAudioSupported = false; bool _isVibrationSupported = false; AudioPool? _popPool; AudioPool? _popAltPool; bool get soundEnabled => _soundEnabled; bool get musicEnabled => _musicEnabled; bool get hapticEnabled => _hapticEnabled; double get bgmVolume => _bgmVolume; double get sfxVolume => _sfxVolume; bool get isAudioSupported => _isAudioSupported; bool get isVibrationSupported => _isVibrationSupported; Future initialize() async { await _detectPlatformCapabilities(); if (_isAudioSupported) { // Preload audio assets try { await FlameAudio.audioCache.loadAll([ 'source/ambient_background.mp3', 'source/bubble_pop.mp3', 'source/bubble_pop_alt.mp3' ]); // Create sound pools for efficient playback _popPool = await FlameAudio.createPool( 'source/bubble_pop.mp3', maxPlayers: 5, ); _popAltPool = await FlameAudio.createPool( 'source/bubble_pop_alt.mp3', maxPlayers: 5, ); } catch (e) { print('Failed to load audio assets or create pools: $e'); } } print('AudioManager initialized for ${Platform.operatingSystem}'); } Future _detectPlatformCapabilities() async { // Check audio support _isAudioSupported = _checkAudioSupport(); // Check vibration support _isVibrationSupported = _checkVibrationSupport(); print('Platform capabilities - Audio: $_isAudioSupported, Vibration: $_isVibrationSupported'); } bool _checkAudioSupport() { try { // Just Audio supports Android, iOS, web, macOS, Windows and Linux // See: https://pub.dev/packages/just_audio return true; } catch (e) { print('Audio detection failed: $e'); return false; } } bool _checkVibrationSupport() { try { // Only Android and iOS support vibration via HapticFeedback if (Platform.isAndroid || Platform.isIOS) { return true; } return false; } catch (e) { print('Vibration detection failed: $e'); return false; } } void playBubblePop() { if (!_soundEnabled) return; // Make audio playback async to avoid blocking main thread _playBubblePopAsync(); // Handle haptic feedback asynchronously if (_hapticEnabled && _isVibrationSupported) { _playHapticFeedbackAsync(); } } void _playBubblePopAsync() async { if (_isAudioSupported) { // Use Flame Audio for bubble pop final sound = _random.nextBool() ? 'source/bubble_pop.mp3' : 'source/bubble_pop_alt.mp3'; try { if (sound == 'source/bubble_pop.mp3' && _popPool != null) { _popPool!.start(volume: _sfxVolume); } else if (sound == 'source/bubble_pop_alt.mp3' && _popAltPool != null) { _popAltPool!.start(volume: _sfxVolume); } else { // Fallback to direct playback if pools fail FlameAudio.play(sound, volume: _sfxVolume); } } catch (e) { print('Failed to play bubble pop sound: $e'); } } else { // Fallback to system sound _playSystemSoundAsync(); } } void _playSystemSoundAsync() async { try { // Vary the system sound for some variety final soundType = _random.nextInt(3); switch (soundType) { case 0: await SystemSound.play(SystemSoundType.click); break; case 1: await SystemSound.play(SystemSoundType.alert); break; default: await SystemSound.play(SystemSoundType.click); } } catch (e) { print('Failed to play system sound: $e'); } } void _playHapticFeedbackAsync() async { if (!_isVibrationSupported) { // Silently skip haptic feedback on unsupported platforms return; } try { // Use Flutter's built-in HapticFeedback instead of vibration plugin await HapticFeedback.lightImpact(); } catch (e) { print('Failed to play haptic feedback: $e'); // Disable vibration support if it fails _isVibrationSupported = false; } } Future playBackgroundMusic() async { if (!_musicEnabled || !_isAudioSupported) { if (!_isAudioSupported) { print('Background music not available - using system audio only'); } return; } try { // Stop any existing BGM first await FlameAudio.bgm.stop(); // Play with updated volume FlameAudio.bgm.play( 'source/ambient_background.mp3', volume: _bgmVolume ); print('Background music started'); } catch (e) { print('Failed to play background music: $e'); } } Future stopBackgroundMusic() async { if (!_isAudioSupported) return; try { FlameAudio.bgm.stop(); } catch (e) { print('Failed to stop background music: $e'); } } Future pauseBackgroundMusic() async { if (!_isAudioSupported) return; try { FlameAudio.bgm.pause(); } catch (e) { print('Failed to pause background music: $e'); } } Future resumeBackgroundMusic() async { if (!_musicEnabled || !_isAudioSupported) return; try { FlameAudio.bgm.resume(); } catch (e) { print('Failed to resume background music: $e'); } } void setSoundEnabled(bool enabled) { _soundEnabled = enabled; } void setMusicEnabled(bool enabled) { _musicEnabled = enabled; if (!enabled) { stopBackgroundMusic(); } else { playBackgroundMusic(); } } void setBgmVolume(double volume) { _bgmVolume = volume; // Volume will be applied when music is next played } void setSfxVolume(double volume) { _sfxVolume = volume; // No effect with system audio } void setHapticEnabled(bool enabled) { _hapticEnabled = enabled; } Future dispose() async { try { await FlameAudio.bgm.dispose(); } catch (e) { print('Failed to dispose background player: $e'); } try { await _popPool?.dispose(); } catch (e) { print('Failed to dispose popPool: $e'); } try { await _popAltPool?.dispose(); } catch (e) { print('Failed to dispose popAltPool: $e'); } } // Utility method to get platform info for debugging String getPlatformInfo() { final audioType = _isAudioSupported ? 'Full Audio Support' : 'System Sounds Only'; return 'Platform: ${Platform.operatingSystem}, ' 'Audio: $audioType, ' 'Vibration: $_isVibrationSupported'; } }