import "dart:async"; import "package:flutter/foundation.dart"; import "package:shared_preferences/shared_preferences.dart"; const String _kWindowShadeKey = "winamp.window_shade"; const String _kAlwaysOnTopKey = "winamp.always_on_top"; const String _kEqOpenKey = "winamp.eq_open"; const String _kEqShadeKey = "winamp.eq_shade"; const String _kPlOpenKey = "winamp.pl_open"; const String _kPlShadeKey = "winamp.pl_shade"; const String _kUiScaleKey = "winamp.ui_scale"; const String _kActiveSkinKey = "winamp.active_skin"; /// Simulated playback/UI state for the player, mirroring the flags tracked by /// the original Winamp main window. No audio yet; the transport simply drives /// the display, sliders, and windowshade. class PlayerState extends ChangeNotifier { PlayerState({ this.windowShade = false, this.alwaysOnTop = false, this.eqOpen = false, this.eqShade = false, this.plOpen = false, this.plShade = false, double uiScale = 1.0, }) : _uiScale = uiScale.clamp(1.0, 3.0); bool playing = false; bool paused = false; bool shuffle = false; bool repeat = false; bool eqOpen = false; bool eqShade = false; bool plOpen = false; bool plShade = false; bool windowShade = false; bool alwaysOnTop = false; bool active = true; double _uiScale = 1.0; /// The window scaling factor (1.0, 2.0, or 3.0) from the Window menu. double get uiScale => _uiScale; /// 0..255, volume.bmp row index maps `volume * 27 / 255`. int volume = 200; /// -127..127; balance.bmp row maps `abs(balance) * 27 / 127`. int balance = 0; /// 0..256 seek position, posbar thumb maps directly. int position = 0; int durationSec = 205; int bitrate = 128; int mixrate = 44; String title = "Winamp 5.9"; String fileName = "dartamp"; int playIcon = 2; /// 0..64 per band, 32 = 0 dB (mirrors the original EQ slider state). int preamp = 32; /// The 10 band gains, index 0 = 60 Hz through 9 = 16 kHz. final List eqBands = List.filled(10, 32); /// Whether the Equalizer is enabled (the ON button). bool eqEnabled = true; /// Whether the Equalizer presets auto-load per song (the AUTO button). bool eqAuto = false; Timer? _ticker; void togglePlay() { if (paused) { paused = false; playing = true; _startTicker(); } else { playing = !playing; paused = false; playIcon = playing ? 0 : 2; if (playing) { _startTicker(); } else { _stopTicker(); } } notifyListeners(); } void pause() { paused = true; playing = false; playIcon = 1; notifyListeners(); } void stop() { playing = false; paused = false; position = 0; playIcon = 2; _stopTicker(); notifyListeners(); } void next() {} void prev() {} void toggleShuffle() { shuffle = !shuffle; notifyListeners(); } void toggleRepeat() { repeat = !repeat; notifyListeners(); } void setVolume(int value) { volume = value.clamp(0, 255); notifyListeners(); } void setBalance(int value) { balance = value.clamp(-127, 127); notifyListeners(); } void setSeek(int value) { position = value.clamp(0, 256); notifyListeners(); } void setPreamp(int value) { final clamped = value.clamp(0, 64); if (clamped == preamp) return; preamp = clamped; notifyListeners(); } void setEqBand(int index, int value) { if (index < 0 || index >= eqBands.length) return; final clamped = value.clamp(0, 64); if (clamped == eqBands[index]) return; eqBands[index] = clamped; notifyListeners(); } void setEqEnabled(bool value) { if (eqEnabled == value) return; eqEnabled = value; notifyListeners(); } void setEqAuto(bool value) { if (eqAuto == value) return; eqAuto = value; notifyListeners(); } void toggleEq() { setEqOpen(!eqOpen); } void setEqOpen(bool value) { if (eqOpen == value) return; eqOpen = value; unawaited(_saveEqOpen(value)); notifyListeners(); } void togglePlaylist() { setPlOpen(!plOpen); } void setPlOpen(bool value) { if (plOpen == value) return; plOpen = value; unawaited(_savePlOpen(value)); notifyListeners(); } void toggleWindowShade() { windowShade = !windowShade; unawaited(_saveWindowShade(windowShade)); notifyListeners(); } void toggleAlwaysOnTop() { setAlwaysOnTop(!alwaysOnTop); } void setAlwaysOnTop(bool value) { if (alwaysOnTop == value) return; alwaysOnTop = value; unawaited(_saveAlwaysOnTop(value)); notifyListeners(); } void setUiScale(double value) { final clamped = value.clamp(1.0, 3.0); if (clamped == _uiScale) return; _uiScale = clamped; unawaited(_saveUiScale(clamped)); notifyListeners(); } void toggleEqShade() { eqShade = !eqShade; unawaited(_saveEqShade(eqShade)); notifyListeners(); } void togglePlShade() { plShade = !plShade; unawaited(_savePlShade(plShade)); notifyListeners(); } Future _saveWindowShade(bool value) async { final prefs = await SharedPreferences.getInstance(); await prefs.setBool(_kWindowShadeKey, value); } Future _saveAlwaysOnTop(bool value) async { final prefs = await SharedPreferences.getInstance(); await prefs.setBool(_kAlwaysOnTopKey, value); } Future _saveEqOpen(bool value) async { final prefs = await SharedPreferences.getInstance(); await prefs.setBool(_kEqOpenKey, value); } Future _saveEqShade(bool value) async { final prefs = await SharedPreferences.getInstance(); await prefs.setBool(_kEqShadeKey, value); } Future _savePlOpen(bool value) async { final prefs = await SharedPreferences.getInstance(); await prefs.setBool(_kPlOpenKey, value); } Future _savePlShade(bool value) async { final prefs = await SharedPreferences.getInstance(); await prefs.setBool(_kPlShadeKey, value); } Future _saveUiScale(double value) async { final prefs = await SharedPreferences.getInstance(); await prefs.setDouble(_kUiScaleKey, value); } static Future loadWindowShade() async { final prefs = await SharedPreferences.getInstance(); return prefs.getBool(_kWindowShadeKey) ?? false; } static Future loadAlwaysOnTop() async { final prefs = await SharedPreferences.getInstance(); return prefs.getBool(_kAlwaysOnTopKey) ?? false; } static Future loadEqOpen() async { final prefs = await SharedPreferences.getInstance(); return prefs.getBool(_kEqOpenKey) ?? false; } static Future loadEqShade() async { final prefs = await SharedPreferences.getInstance(); return prefs.getBool(_kEqShadeKey) ?? false; } static Future loadPlOpen() async { final prefs = await SharedPreferences.getInstance(); return prefs.getBool(_kPlOpenKey) ?? false; } static Future loadPlShade() async { final prefs = await SharedPreferences.getInstance(); return prefs.getBool(_kPlShadeKey) ?? false; } static Future loadUiScale() async { final prefs = await SharedPreferences.getInstance(); return (prefs.getDouble(_kUiScaleKey) ?? 1.0).clamp(1.0, 3.0); } /// The name of the active skin, or `null` when the default (Base) applies. static Future loadActiveSkin() async { final prefs = await SharedPreferences.getInstance(); return prefs.getString(_kActiveSkinKey); } /// Persists the active skin name; `null` restores the default. static Future saveActiveSkin(String? name) async { final prefs = await SharedPreferences.getInstance(); if (name == null) { await prefs.remove(_kActiveSkinKey); } else { await prefs.setString(_kActiveSkinKey, name); } } void _startTicker() { _ticker?.cancel(); _ticker = Timer.periodic(const Duration(seconds: 1), (_) { final nextSec = position * durationSec ~/ 256 + 1; position = nextSec >= durationSec ? 0 : nextSec * 256 ~/ durationSec; playIcon = 0; notifyListeners(); }); } void _stopTicker() { _ticker?.cancel(); _ticker = null; } @override void dispose() { _stopTicker(); super.dispose(); } }