90 lines
3.7 KiB
Dart
90 lines
3.7 KiB
Dart
import 'package:flame_audio/flame_audio.dart';
|
|
|
|
/// سرویسِ متمرکزِ افکتهای صوتی (فایلها در assets/audio/).
|
|
///
|
|
/// از AudioPool استفاده میشود: برای هر صدا یک مجموعهی محدود از پخشکنندهها
|
|
/// ساخته و بازاستفاده میشود. این کار از نشتِ AudioPlayerها (که روی اندروید
|
|
/// باعثِ قطعِ کاملِ صدا و پرشِ فریم میشد) جلوگیری میکند.
|
|
class AppSounds {
|
|
AppSounds._();
|
|
|
|
static const _shuffle = 'shuffle.mp3'; // بُر زدن/پخشِ کارتها (شروع دست)
|
|
static const _placeCard = 'place_card.mp3'; // گذاشتنِ کارت روی میز (همهی بازیکنان)
|
|
static const _takeCard = 'take_card.mp3'; // جمعشدنِ دستِ کامل
|
|
static const _cut = 'cut.mp3'; // بریدن با حکم (شمشیر)
|
|
static const _win = 'win.mp3'; // برد بازی
|
|
static const _button = 'button.mp3'; // فشردنِ دکمهها
|
|
static const _selectHokm = 'select_hokm.mp3'; // انتخابِ حکم
|
|
static const _heartBeat = 'heart_beat.mp3'; // ثانیههای پایانیِ نوبت
|
|
static const _turn = 'turn.mp3'; // شروعِ نوبتِ خودِ کاربر
|
|
|
|
static const _all = [
|
|
_shuffle,
|
|
_placeCard,
|
|
_takeCard,
|
|
_cut,
|
|
_win,
|
|
_button,
|
|
_selectHokm,
|
|
_heartBeat,
|
|
_turn,
|
|
];
|
|
|
|
static final Map<String, AudioPool> _pools = {};
|
|
static bool muted = false;
|
|
|
|
/// ساختِ Poolها (یکبار، در main). تا آمادهشدن، پخشها بیاثرند.
|
|
static Future<void> preload() async {
|
|
for (final f in _all) {
|
|
try {
|
|
_pools[f] = await FlameAudio.createPool(f, maxPlayers: 3);
|
|
} catch (_) {/* نبودِ فایل ⇒ بیصدا */}
|
|
}
|
|
}
|
|
|
|
/// پخش از Pool؛ اگر cutoffMs داده شود، صدا پس از آن مدت قطع میشود (کلیکِ کوتاه).
|
|
static void _play(String file, double volume, {int? cutoffMs}) {
|
|
if (muted) return;
|
|
final pool = _pools[file];
|
|
if (pool == null) return;
|
|
pool.start(volume: volume).then((stop) {
|
|
if (cutoffMs != null) {
|
|
Future.delayed(Duration(milliseconds: cutoffMs), () => stop());
|
|
}
|
|
}).catchError((_) {});
|
|
}
|
|
|
|
/// صدای بُر زدن؛ cutoffMs را برابرِ مدتِ انیمیشنِ بُر بده تا همزمان تمام شوند.
|
|
static void shuffle({int? cutoffMs}) => _play(_shuffle, 0.7, cutoffMs: cutoffMs);
|
|
static void placeCard() => _play(_placeCard, 0.8);
|
|
static void takeCard() => _play(_takeCard, 0.7);
|
|
static void cut() => _play(_cut, 0.9);
|
|
static void win() => _play(_win, 0.9);
|
|
static void selectHokm() => _play(_selectHokm, 0.9);
|
|
static void turn() => _play(_turn, 0.9); // نوبتِ کاربر برای بازی
|
|
// شمارشِ معکوسِ شروعِ بازی (۳…۲…۱) — کلیکِ کوتاه برای هر عدد.
|
|
static void tick() => _play(_button, 0.7, cutoffMs: 200);
|
|
|
|
// کنترلِ ضربانِ قلب: همیشه حداکثر یک ضربان همزمان (بدون انباشت/تکرارِ سریع)
|
|
// و قابلِ توقف وقتی نوبت تمام میشود.
|
|
static Function? _hbStop;
|
|
|
|
static void heartBeat() {
|
|
final pool = _pools[_heartBeat];
|
|
if (muted || pool == null) return;
|
|
_hbStop?.call(); // قطعِ ضربانِ قبلی پیش از پخشِ جدید
|
|
_hbStop = null;
|
|
pool.start(volume: 0.8).then((stop) {
|
|
_hbStop = stop;
|
|
}, onError: (_) {});
|
|
}
|
|
|
|
static void stopHeartBeat() {
|
|
try {
|
|
_hbStop?.call();
|
|
} catch (_) {}
|
|
_hbStop = null;
|
|
}
|
|
static void button() => _play(_button, 0.5, cutoffMs: 130); // کلیکِ کوتاه
|
|
}
|