Files
dartamp/lib/src/player/player_state.dart
T
hans 2f5edfc512 feat: update dependencies and integrate skin manager
- Added `dartamp_skin` as a local dependency in `pubspec.yaml`.
- Updated `path_provider` to version ^2.1.5.
- Modified `test_driver/app.dart` to initialize `SkinManager` and pass it to `DartampApp`.
- Updated `generated_plugins.cmake` to include `jni` in the FFI plugin list.

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
2026-08-09 21:14:54 +02:00

332 lines
8.1 KiB
Dart

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<int> 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<void> _saveWindowShade(bool value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_kWindowShadeKey, value);
}
Future<void> _saveAlwaysOnTop(bool value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_kAlwaysOnTopKey, value);
}
Future<void> _saveEqOpen(bool value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_kEqOpenKey, value);
}
Future<void> _saveEqShade(bool value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_kEqShadeKey, value);
}
Future<void> _savePlOpen(bool value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_kPlOpenKey, value);
}
Future<void> _savePlShade(bool value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_kPlShadeKey, value);
}
Future<void> _saveUiScale(double value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setDouble(_kUiScaleKey, value);
}
static Future<bool> loadWindowShade() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getBool(_kWindowShadeKey) ?? false;
}
static Future<bool> loadAlwaysOnTop() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getBool(_kAlwaysOnTopKey) ?? false;
}
static Future<bool> loadEqOpen() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getBool(_kEqOpenKey) ?? false;
}
static Future<bool> loadEqShade() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getBool(_kEqShadeKey) ?? false;
}
static Future<bool> loadPlOpen() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getBool(_kPlOpenKey) ?? false;
}
static Future<bool> loadPlShade() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getBool(_kPlShadeKey) ?? false;
}
static Future<double> 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<String?> loadActiveSkin() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString(_kActiveSkinKey);
}
/// Persists the active skin name; `null` restores the default.
static Future<void> 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();
}
}