ad6689b42f
Pure-Dart WAV synthesizer (tool/gen_sfx.dart) generates place/clear/ combo/win/lose effects; AudioService player pool fires on placement, line clears, combo streaks, and phase transitions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
43 lines
1021 B
Dart
43 lines
1021 B
Dart
import 'package:audioplayers/audioplayers.dart';
|
|
|
|
enum Sfx { place, clear, combo, win, lose }
|
|
|
|
/// Fire-and-forget SFX. A small player pool avoids cutting sounds off when
|
|
/// effects overlap (place + clear on the same move).
|
|
class AudioService {
|
|
AudioService({this.enabled = true});
|
|
|
|
bool enabled;
|
|
|
|
static const _files = {
|
|
Sfx.place: 'audio/place.wav',
|
|
Sfx.clear: 'audio/clear.wav',
|
|
Sfx.combo: 'audio/combo.wav',
|
|
Sfx.win: 'audio/win.wav',
|
|
Sfx.lose: 'audio/lose.wav',
|
|
};
|
|
|
|
final _players = <AudioPlayer>[
|
|
for (var i = 0; i < 3; i++) AudioPlayer()..setReleaseMode(ReleaseMode.stop),
|
|
];
|
|
var _next = 0;
|
|
|
|
Future<void> play(Sfx sfx) async {
|
|
if (!enabled) return;
|
|
final player = _players[_next];
|
|
_next = (_next + 1) % _players.length;
|
|
try {
|
|
await player.stop();
|
|
await player.play(AssetSource(_files[sfx]!));
|
|
} catch (_) {
|
|
// Audio must never break gameplay.
|
|
}
|
|
}
|
|
|
|
void dispose() {
|
|
for (final p in _players) {
|
|
p.dispose();
|
|
}
|
|
}
|
|
}
|