From 755e75231de92f8d022a876165845266429107a2 Mon Sep 17 00:00:00 2001 From: Leptopoda Date: Sun, 10 Oct 2021 20:02:59 +0200 Subject: [PATCH] fix sound on game navigation Effectively rewriting the entire audio stack - propperly await futures - only play sound once on take play back Further testing is needed but as we await the autio play we should be able to remove the stopPlay now. fix late init problem --- src/ui/flutter_app/lib/mill/game.dart | 7 +- src/ui/flutter_app/lib/mill/position.dart | 148 ++++++----- .../lib/screens/game_page/game_page.dart | 109 ++++---- src/ui/flutter_app/lib/services/audios.dart | 248 ++++++++---------- .../lib/shared/drawer_controller.dart | 2 +- 5 files changed, 251 insertions(+), 263 deletions(-) diff --git a/src/ui/flutter_app/lib/mill/game.dart b/src/ui/flutter_app/lib/mill/game.dart index d8b497fb..5d65ec26 100644 --- a/src/ui/flutter_app/lib/mill/game.dart +++ b/src/ui/flutter_app/lib/mill/game.dart @@ -32,6 +32,7 @@ class Game { final String tag = "[game]"; void init() { + // TODO: _position is allready initialized with Position(). seems like duplicate code _position = Position(); focusIndex = blurIndex = invalidIndex; } @@ -71,7 +72,7 @@ class Game { PieceColor.black: false }; - bool aiIsSearching() { + bool get aiIsSearching { debugPrint( "$tag White is searching? ${isSearching[PieceColor.white]}\n" "$tag Black is searching? ${isSearching[PieceColor.black]}\n", @@ -115,14 +116,14 @@ class Game { blurIndex = invalidIndex; } - bool doMove(String move) { + Future doMove(String move) async { if (position.phase == Phase.ready) { start(); } debugPrint("$tag AI do move: $move"); - if (!position.doMove(move)) { + if (await position.doMove(move) == false) { return false; } diff --git a/src/ui/flutter_app/lib/mill/position.dart b/src/ui/flutter_app/lib/mill/position.dart index a219e540..5e6415c6 100644 --- a/src/ui/flutter_app/lib/mill/position.dart +++ b/src/ui/flutter_app/lib/mill/position.dart @@ -25,6 +25,7 @@ import 'package:sanmill/mill/types.dart'; import 'package:sanmill/mill/zobrist.dart'; import 'package:sanmill/services/audios.dart'; import 'package:sanmill/services/engine/engine.dart'; +import 'package:sanmill/shared/common/config.dart'; List posKeyHistory = []; @@ -62,7 +63,7 @@ class Position { String us = PieceColor.white; String them = PieceColor.black; - String winner = PieceColor.nobody; + String _winner = PieceColor.nobody; GameOverReason gameOverReason = GameOverReason.noReason; @@ -114,7 +115,7 @@ class Position { st.pliesFromNull = other.st.pliesFromNull; them = other.them; - winner = other.winner; + _winner = other._winner; gameOverReason = other.gameOverReason; phase = other.phase; @@ -135,7 +136,7 @@ class Position { String movedPiece(int move) => pieceOn(fromSq(move)); - bool movePiece(int from, int to) { + Future movePiece(int from, int to) async { if (selectPiece(from) == 0) { return putPiece(to); } @@ -261,7 +262,7 @@ class Position { return true; } - bool doMove(String move) { + Future doMove(String move) async { if (move.length > "Player".length && move.substring(0, "Player".length - 1) == "Player") { if (move["Player".length] == '1') { @@ -278,7 +279,7 @@ class Position { if (move == "draw") { phase = Phase.gameOver; - winner = PieceColor.draw; + _winner = PieceColor.draw; if (score[PieceColor.draw] != null) { score[PieceColor.draw] = score[PieceColor.draw]! + 1; } @@ -287,7 +288,7 @@ class Position { if (rule.nMoveRule > 0 && posKeyHistory.length >= rule.nMoveRule - 1) { gameOverReason = GameOverReason.drawReasonRule50; } else if (rule.endgameNMoveRule < rule.nMoveRule && - isThreeEndgame() && + isThreeEndgame && posKeyHistory.length >= rule.endgameNMoveRule - 1) { gameOverReason = GameOverReason.drawReasonEndgameRule50; } else if (rule.threefoldRepetitionRule) { @@ -308,20 +309,20 @@ class Position { switch (m.type) { case MoveType.remove: - ret = removePiece(m.to) == 0; + ret = await removePiece(m.to) == 0; if (ret) { // Reset rule 50 counter st.rule50 = 0; } break; case MoveType.move: - ret = movePiece(m.from, m.to); + ret = await movePiece(m.from, m.to); if (ret) { ++st.rule50; } break; case MoveType.place: - ret = putPiece(m.to); + ret = await putPiece(m.to); if (ret) { // Reset rule 50 counter st.rule50 = 0; @@ -414,7 +415,7 @@ class Position { setSideToMove(PieceColor.white); action = Act.place; - winner = PieceColor.nobody; + _winner = PieceColor.nobody; gameOverReason = GameOverReason.noReason; clearBoard(); @@ -457,7 +458,7 @@ class Position { } } - bool putPiece(int s) { + Future putPiece(int s) async { var piece = Piece.noPiece; final us = _sideToMove; @@ -472,6 +473,7 @@ class Position { start(); } + // TODO: use switch case if (phase == Phase.placing) { piece = sideToMove; if (pieceInHandCount[us] != null) { @@ -501,7 +503,7 @@ class Position { if (pieceInHandCount[PieceColor.white] == 0 && pieceInHandCount[PieceColor.black] == 0) { - if (checkIfGameIsOver()) { + if (isGameOver()) { return true; } @@ -516,14 +518,14 @@ class Position { changeSideToMove(); } - if (checkIfGameIsOver()) { + if (isGameOver()) { return true; } } else { changeSideToMove(); } gameInstance.focusIndex = squareToIndex[s] ?? invalidIndex; - Audios.playTone(Audios.placeSoundId); + await Audios.playTone(Sound.place); } else { pieceToRemoveCount = rule.mayRemoveMultiple ? n : 1; updateKeyMisc(); @@ -539,7 +541,7 @@ class Position { if (pieceInHandCount[PieceColor.white] == 0 && pieceInHandCount[PieceColor.black] == 0) { - if (checkIfGameIsOver()) { + if (isGameOver()) { return true; } @@ -550,7 +552,7 @@ class Position { changeSideToMove(); } - if (checkIfGameIsOver()) { + if (isGameOver()) { return true; } } @@ -559,10 +561,10 @@ class Position { } gameInstance.focusIndex = squareToIndex[s] ?? invalidIndex; - Audios.playTone(Audios.millSoundId); + await Audios.playTone(Sound.mill); } } else if (phase == Phase.moving) { - if (checkIfGameIsOver()) { + if (isGameOver()) { return true; } @@ -603,18 +605,18 @@ class Position { action = Act.select; changeSideToMove(); - if (checkIfGameIsOver()) { + if (isGameOver()) { return true; } else { gameInstance.focusIndex = squareToIndex[s] ?? invalidIndex; - Audios.playTone(Audios.placeSoundId); + await Audios.playTone(Sound.place); } } else { pieceToRemoveCount = rule.mayRemoveMultiple ? n : 1; updateKeyMisc(); action = Act.remove; gameInstance.focusIndex = squareToIndex[s] ?? invalidIndex; - Audios.playTone(Audios.millSoundId); + await Audios.playTone(Sound.mill); } } else { assert(false); @@ -623,7 +625,7 @@ class Position { return true; } - int removePiece(int s) { + Future removePiece(int s) async { if (phase == Phase.ready || phase == Phase.gameOver) return -1; if (action != Act.remove) return -1; @@ -641,7 +643,7 @@ class Position { revertKey(s); - Audios.playTone(Audios.removeSoundId); + await Audios.playTone(Sound.remove); if (rule.hasBannedLocations && phase == Phase.placing) { // Remove and put ban @@ -685,7 +687,7 @@ class Position { } if (rule.isDefenderMoveFirst) { - checkIfGameIsOver(); + isGameOver(); return 0; } } else { @@ -696,7 +698,7 @@ class Position { } changeSideToMove(); - checkIfGameIsOver(); + isGameOver(); return 0; } @@ -733,14 +735,12 @@ class Position { return true; } - String getWinner() { - return winner; - } + String get winner => _winner; void setGameOver(String w, GameOverReason reason) { phase = Phase.gameOver; gameOverReason = reason; - winner = w; + _winner = w; debugPrint("[position] Game over, $w win, because of $reason"); updateScore(); @@ -748,7 +748,7 @@ class Position { void updateScore() { if (phase == Phase.gameOver) { - if (winner == PieceColor.draw) { + if (_winner == PieceColor.draw) { if (score[PieceColor.draw] != null) { score[PieceColor.draw] = score[PieceColor.draw]! + 1; } @@ -756,13 +756,13 @@ class Position { return; } - if (score[winner] != null) { - score[winner] = score[winner]! + 1; + if (score[_winner] != null) { + score[_winner] = score[_winner]! + 1; } } } - bool isThreeEndgame() { + bool get isThreeEndgame { if (phase == Phase.placing) { return false; } @@ -771,7 +771,7 @@ class Position { pieceOnBoardCount[PieceColor.black] == 3; } - bool checkIfGameIsOver() { + bool isGameOver() { if (phase == Phase.ready || phase == Phase.gameOver) { return true; } @@ -782,7 +782,7 @@ class Position { } if (rule.endgameNMoveRule < rule.nMoveRule && - isThreeEndgame() && + isThreeEndgame && posKeyHistory.length >= rule.endgameNMoveRule) { setGameOver(PieceColor.draw, GameOverReason.drawReasonEndgameRule50); return true; @@ -1047,7 +1047,7 @@ class Position { nPiecesInHand; pieceToRemoveCount = 0; - winner = PieceColor.nobody; + _winner = PieceColor.nobody; Mills.adjacentSquaresInit(); Mills.millTableInit(); currentSquare = 0; @@ -1086,25 +1086,29 @@ class Position { } /////////////////////////////////////////////////////////////////////////////// - - Future _gotoHistory(int moveIndex) async { - String errString = ""; + Future gotoHistory(HistoryMove move, [int? index]) async { + final int moveIndex = _gotoHistoryIndex(move, index); if (recorder == null) { debugPrint("[goto] recorder is null."); return "null"; } + if (recorder!.cur == moveIndex) { + debugPrint("[goto] cur is equal to moveIndex."); + return "equal"; + } + final history = recorder!.history; + if (moveIndex < -1 || history.length <= moveIndex) { debugPrint("[goto] moveIndex is out of range."); return "out-of-range"; } - if (recorder!.cur == moveIndex) { - debugPrint("[goto] cur is equal to moveIndex."); - return "equal"; - } + String errString = ""; + + Audios.isTemporaryMute = true; // Backup context final engineTypeBackup = gameInstance.engineType; @@ -1121,13 +1125,10 @@ class Position { } for (var i = 0; i <= moveIndex; i++) { - if (gameInstance.doMove(history[i].move!) == false) { + if (await gameInstance.doMove(history[i].move!) == false) { errString = history[i].move!; break; } - - //await Future.delayed(Duration(seconds: 1)); - //setState(() {}); } // Restore context @@ -1136,31 +1137,46 @@ class Position { recorder!.history = historyBack; recorder!.cur = moveIndex; + Audios.isTemporaryMute = false; + await _gotoHistoryPlaySound(move); + return errString; } - Future takeBackN(int n) async { - int index = recorder!.cur - n; - if (index < -1) { - index = -1; + int _gotoHistoryIndex(HistoryMove move, [int? index]) { + switch (move) { + case HistoryMove.forwardAll: + return recorder!.history.length - 1; + case HistoryMove.backAll: + return -1; + case HistoryMove.farward: + return recorder!.cur + 1; + case HistoryMove.backN: + assert(index != null); + int _index = recorder!.cur - index!; + if (_index < -1) { + _index = -1; + } + return _index; + case HistoryMove.backOne: + return recorder!.cur - 1; } - return _gotoHistory(index); } - Future takeBack() async { - return _gotoHistory(recorder!.cur - 1); - } + Future _gotoHistoryPlaySound(HistoryMove move) async { + if (!Config.keepMuteWhenTakingBack) { + switch (move) { + case HistoryMove.forwardAll: + case HistoryMove.farward: + await Audios.playTone(Sound.place); + break; + case HistoryMove.backAll: + case HistoryMove.backN: - Future stepForward() async { - return _gotoHistory(recorder!.cur + 1); - } - - Future takeBackAll() async { - return _gotoHistory(-1); - } - - Future stepForwardAll() async { - return _gotoHistory(recorder!.history.length - 1); + case HistoryMove.backOne: + await Audios.playTone(Sound.remove); + } + } } String movesSinceLastRemove() { @@ -1205,3 +1221,5 @@ class Position { String? get lastPositionWithRemove => recorder!.lastPositionWithRemove; } + +enum HistoryMove { forwardAll, backAll, farward, backN, backOne } diff --git a/src/ui/flutter_app/lib/screens/game_page/game_page.dart b/src/ui/flutter_app/lib/screens/game_page/game_page.dart index 8375a3d1..9b6797cd 100644 --- a/src/ui/flutter_app/lib/screens/game_page/game_page.dart +++ b/src/ui/flutter_app/lib/screens/game_page/game_page.dart @@ -172,7 +172,7 @@ class _GamePageState extends State } } - dynamic onBoardTap(int index) { + Future onBoardTap(int index) async { if (!isReady) { debugPrint("[tap] Not ready, ignore tapping."); return false; @@ -208,18 +208,18 @@ class _GamePageState extends State gameInstance.newGame(); if (gameInstance.isAiToMove) { - if (gameInstance.aiIsSearching()) { + if (gameInstance.aiIsSearching) { debugPrint("$tag AI is thinking, skip tapping."); return false; } else { debugPrint("[tap] AI is not thinking. AI is to move."); - engineToGo(false); + await engineToGo(false); return false; } } } - if (gameInstance.isAiToMove || gameInstance.aiIsSearching()) { + if (gameInstance.isAiToMove || gameInstance.aiIsSearching) { debugPrint("[tap] AI's turn, skip tapping."); return false; } @@ -231,12 +231,12 @@ class _GamePageState extends State // Human to go bool ret = false; - Chain.capture(() { + await Chain.capture(() async { switch (position.action) { case Act.place: - if (position.putPiece(sq)) { + if (await position.putPiece(sq)) { if (position.action == Act.remove) { - //Audios.playTone(Audios.millSoundId); + //Audios.playTone(Audios.mill); if (mounted) { showTip(S.of(context).tipMill); if (Config.screenReaderSupport) { @@ -244,7 +244,7 @@ class _GamePageState extends State } } } else { - //Audios.playTone(Audios.placeSoundId); + //Audios.playTone(Audios.place); if (gameInstance.engineType == EngineType.humanVsAi && mounted) { if (rule.mayOnlyRemoveUnplacedPieceInPlacingPhase) { showTip(S.of(context).continueToMakeMove); @@ -296,7 +296,7 @@ class _GamePageState extends State final int selectRet = position.selectPiece(sq); switch (selectRet) { case 0: - Audios.playTone(Audios.selectSoundId); + await Audios.playTone(Sound.select); gameInstance.select(index); ret = true; debugPrint("[tap] selectPiece: [$sq]"); @@ -325,7 +325,7 @@ class _GamePageState extends State break; case -2: - Audios.playTone(Audios.illegalSoundId); + await Audios.playTone(Sound.illegal); debugPrint("[tap] selectPiece: skip [$sq]"); if (mounted && position.phase != Phase.gameOver) { showTip(S.of(context).tipCannotMove); @@ -336,7 +336,7 @@ class _GamePageState extends State } break; case -3: - Audios.playTone(Audios.illegalSoundId); + await Audios.playTone(Sound.illegal); debugPrint("[tap] selectPiece: skip [$sq]"); if (mounted) { showTip(S.of(context).tipCanMoveOnePoint); @@ -347,7 +347,7 @@ class _GamePageState extends State } break; case -4: - Audios.playTone(Audios.illegalSoundId); + await Audios.playTone(Sound.illegal); debugPrint("[tap] selectPiece: skip [$sq]"); if (mounted) { showTip(S.of(context).tipSelectPieceToMove); @@ -357,7 +357,7 @@ class _GamePageState extends State } break; default: - Audios.playTone(Audios.illegalSoundId); + await Audios.playTone(Sound.illegal); debugPrint("[tap] selectPiece: skip [$sq]"); if (mounted) { showTip(S.of(context).tipSelectWrong); @@ -372,11 +372,11 @@ class _GamePageState extends State break; case Act.remove: - final int removeRet = position.removePiece(sq); + final int removeRet = await position.removePiece(sq); switch (removeRet) { case 0: - //Audios.playTone(Audios.removeSoundId); + //Audios.playTone(Audios.remove); ret = true; debugPrint("[tap] removePiece: [$sq]"); if (gameInstance.position.pieceToRemoveCount >= 1) { @@ -404,7 +404,7 @@ class _GamePageState extends State } break; case -2: - Audios.playTone(Audios.illegalSoundId); + await Audios.playTone(Sound.illegal); debugPrint( "[tap] removePiece: Cannot Remove our pieces, skip [$sq]", ); @@ -416,7 +416,7 @@ class _GamePageState extends State } break; case -3: - Audios.playTone(Audios.illegalSoundId); + await Audios.playTone(Sound.illegal); debugPrint( "[tap] removePiece: Cannot remove piece from Mill, skip [$sq]", ); @@ -432,7 +432,7 @@ class _GamePageState extends State } break; default: - Audios.playTone(Audios.illegalSoundId); + await Audios.playTone(Sound.illegal); debugPrint("[tap] removePiece: skip [$sq]"); if (mounted && position.phase != Phase.gameOver) { showTip(S.of(context).tipBanRemove); @@ -587,7 +587,7 @@ class _GamePageState extends State ); } - gameInstance.doMove(move.move!); + await gameInstance.doMove(move.move!); showTips(); if (Config.screenReaderSupport && move.notation != null) { showSnackBar(context, "${S.of(context).ai}: ${move.notation!}"); @@ -657,7 +657,7 @@ class _GamePageState extends State debugPrint("Clipboard text:"); debugPrint(text); - await onTakeBackAllButtonPressed(pop: false); + await onTakeBackAllButtonPressed(false); gameInstance.position.recorder!.clear(); final importFailedStr = gameInstance.position.recorder!.import(text); @@ -670,7 +670,7 @@ class _GamePageState extends State return; } - await onStepForwardAllButtonPressed(pop: false); + await onStepForwardAllButtonPressed(false); showTip(S.of(context).gameImported); if (Config.screenReaderSupport) { @@ -774,8 +774,8 @@ class _GamePageState extends State Future onAutoReplayButtonPressed() async { Navigator.pop(context); - await onTakeBackAllButtonPressed(pop: false); - await onStepForwardAllButtonPressed(pop: false); + await onTakeBackAllButtonPressed(false); + await onStepForwardAllButtonPressed(false); } void onGameButtonPressed() { @@ -969,8 +969,9 @@ class _GamePageState extends State } Future onGotoHistoryButtonsPressed( - Future func, { + HistoryMove move, { bool pop = true, + int? number, }) async { if (pop == true) { Navigator.pop(context); @@ -989,9 +990,7 @@ class _GamePageState extends State isGoingToHistory = true; - Audios.isTemporaryMute = Config.keepMuteWhenTakingBack; - - final errMove = await func; + final errMove = await gameInstance.position.gotoHistory(move, number); switch (errMove) { case "": @@ -1008,14 +1007,10 @@ class _GamePageState extends State break; } - Audios.isTemporaryMute = false; - isGoingToHistory = false; if (mounted) { - String text = ""; final pos = gameInstance.position; - /* String us = ""; String them = ""; @@ -1028,6 +1023,7 @@ class _GamePageState extends State } */ + late final String text; final lastEffectiveMove = pos.recorder!.lastEffectiveMove; if (lastEffectiveMove != null && lastEffectiveMove.notation != null) { text = "${S.of(context).lastMove}: ${lastEffectiveMove.notation}"; @@ -1044,28 +1040,37 @@ class _GamePageState extends State } } - Future onTakeBackButtonPressed({bool pop = true}) async { - onGotoHistoryButtonsPressed(gameInstance.position.takeBack(), pop: pop); - } + Future onTakeBackButtonPressed([bool pop = true]) async => + onGotoHistoryButtonsPressed( + HistoryMove.backOne, + pop: pop, + ); - Future onStepForwardButtonPressed({bool pop = true}) async { - onGotoHistoryButtonsPressed(gameInstance.position.stepForward(), pop: pop); - } + Future onStepForwardButtonPressed([bool pop = true]) async => + onGotoHistoryButtonsPressed( + HistoryMove.farward, + pop: pop, + ); - Future onTakeBackAllButtonPressed({bool pop = true}) async { - onGotoHistoryButtonsPressed(gameInstance.position.takeBackAll(), pop: pop); - } + Future onTakeBackAllButtonPressed([bool pop = true]) async => + onGotoHistoryButtonsPressed( + HistoryMove.backAll, + pop: pop, + ); - Future onStepForwardAllButtonPressed({bool pop = true}) async { + Future onStepForwardAllButtonPressed([bool pop = true]) async { onGotoHistoryButtonsPressed( - gameInstance.position.stepForwardAll(), + HistoryMove.forwardAll, pop: pop, ); } - Future onTakeBackNButtonPressed(int n, {bool pop = true}) async { - onGotoHistoryButtonsPressed(gameInstance.position.takeBackN(n), pop: pop); - } + Future onTakeBackNButtonPressed(int n, [bool pop = true]) async => + onGotoHistoryButtonsPressed( + HistoryMove.backN, + number: n, + pop: pop, + ); void onMoveListButtonPressed() { final moveHistoryText = gameInstance.position.moveHistoryText; @@ -1177,7 +1182,7 @@ class _GamePageState extends State debugPrint("[config] isPrivacyPolicyAccepted: $value"); - Config.save(); + await Config.save(); } Future onShowPrivacyDialog() async { @@ -1259,10 +1264,10 @@ class _GamePageState extends State switch (result) { case GameResult.win: - //Audios.playTone(Audios.winSoundId); + //Audios.playTone(Audios.win); break; case GameResult.lose: - //Audios.playTone(Audios.loseSoundId); + //Audios.playTone(Audios.lose); break; case GameResult.draw: break; @@ -1694,7 +1699,7 @@ class _GamePageState extends State color: Color(Config.navigationToolbarIconColor), ), ), - onPressed: () => onTakeBackAllButtonPressed(pop: false), + onPressed: () => onTakeBackAllButtonPressed(false), ); final takeBackButton = TextButton( @@ -1707,7 +1712,7 @@ class _GamePageState extends State color: Color(Config.navigationToolbarIconColor), ), ), - onPressed: () => onTakeBackButtonPressed(pop: false), + onPressed: () async => onTakeBackButtonPressed(false), ); final stepForwardButton = TextButton( @@ -1720,7 +1725,7 @@ class _GamePageState extends State color: Color(Config.navigationToolbarIconColor), ), ), - onPressed: () => onStepForwardButtonPressed(pop: false), + onPressed: () async => onStepForwardButtonPressed(false), ); final stepForwardAllButton = TextButton( @@ -1733,7 +1738,7 @@ class _GamePageState extends State color: Color(Config.navigationToolbarIconColor), ), ), - onPressed: () => onStepForwardAllButtonPressed(pop: false), + onPressed: () async => onStepForwardAllButtonPressed(false), ); return GamePageToolBar( diff --git a/src/ui/flutter_app/lib/services/audios.dart b/src/ui/flutter_app/lib/services/audios.dart index 1c7dabbc..07f8d907 100644 --- a/src/ui/flutter_app/lib/services/audios.dart +++ b/src/ui/flutter_app/lib/services/audios.dart @@ -24,22 +24,35 @@ import 'package:sanmill/shared/common/config.dart'; import 'package:soundpool/soundpool.dart'; import 'package:stack_trace/stack_trace.dart'; +enum Sound { + draw, + fly, + go, + illegal, + lose, + mill, + place, + remove, + select, + win, +} + class Audios { const Audios._(); //static AudioPlayer? _player; - static Soundpool? _soundpool; - // TODO: use enum for the sounds - static int? _alarmSoundStreamId; - static int? drawSoundId; - static int? flySoundId; - static int? goSoundId; - static int? illegalSoundId; - static int? loseSoundId; - static int? millSoundId; - static int? placeSoundId; - static int? removeSoundId; - static int? selectSoundId; - static int? winSoundId; + static final Soundpool _soundpool = Soundpool.fromOptions(); + static bool _initialized = false; + static int _alarmSoundStreamId = 0; + static late final int _drawSoundId; + static late final int _flySoundId; + static late final int _goSoundId; + static late final int _illegalSoundId; + static late final int _loseSoundId; + static late final int _millSoundId; + static late final int _placeSoundId; + static late final int _removeSoundId; + static late final int _selectSoundId; + static late final int _winSoundId; static bool isTemporaryMute = false; static Future loadSounds() async { @@ -48,156 +61,110 @@ class Audios { return; } - _soundpool ??= Soundpool.fromOptions(); + _drawSoundId = await _soundpool.load( + await rootBundle.load("assets/audios/draw.mp3"), + ); - if (_soundpool == null) { - if (Config.developerMode) { - assert(false); - } - debugPrint("[audio] Error: _soundpool is null."); - return; - } + _flySoundId = await _soundpool.load( + await rootBundle.load("assets/audios/fly.mp3"), + ); - drawSoundId ??= - await _soundpool!.load(await rootBundle.load("assets/audios/draw.mp3")); - if (drawSoundId == null) { - if (Config.developerMode) { - assert(false); - } - debugPrint("[audio] Error: drawSoundId is null."); - return; - } + _goSoundId = await _soundpool.load( + await rootBundle.load("assets/audios/go.mp3"), + ); - flySoundId ??= - await _soundpool!.load(await rootBundle.load("assets/audios/fly.mp3")); - if (flySoundId == null) { - if (Config.developerMode) { - assert(false); - } - debugPrint("[audio] Error: flySoundId is null."); - return; - } + _illegalSoundId = await _soundpool.load( + await rootBundle.load("assets/audios/illegal.mp3"), + ); - goSoundId ??= - await _soundpool!.load(await rootBundle.load("assets/audios/go.mp3")); - if (goSoundId == null) { - if (Config.developerMode) { - assert(false); - } - debugPrint("[audio] Error: goSoundId is null."); - return; - } + _loseSoundId = await _soundpool.load( + await rootBundle.load("assets/audios/lose.mp3"), + ); - illegalSoundId = await _soundpool! - .load(await rootBundle.load("assets/audios/illegal.mp3")); - if (illegalSoundId == null) { - if (Config.developerMode) { - assert(false); - } - debugPrint("[audio] Error: illegalSoundId is null."); - return; - } + _millSoundId = await _soundpool.load( + await rootBundle.load("assets/audios/mill.mp3"), + ); - loseSoundId ??= - await _soundpool!.load(await rootBundle.load("assets/audios/lose.mp3")); - if (loseSoundId == null) { - if (Config.developerMode) { - assert(false); - } - debugPrint("[audio] Error: loseSoundId is null."); - return; - } + _placeSoundId = await _soundpool.load( + await rootBundle.load("assets/audios/place.mp3"), + ); - millSoundId ??= - await _soundpool!.load(await rootBundle.load("assets/audios/mill.mp3")); - if (millSoundId == null) { - if (Config.developerMode) { - assert(false); - } - debugPrint("[audio] Error: millSoundId is null."); - return; - } + _removeSoundId = await _soundpool.load( + await rootBundle.load("assets/audios/remove.mp3"), + ); - placeSoundId ??= await _soundpool! - .load(await rootBundle.load("assets/audios/place.mp3")); - if (placeSoundId == null) { - if (Config.developerMode) { - assert(false); - } - debugPrint("[audio] Error: placeSoundId is null."); - return; - } + _selectSoundId = await _soundpool.load( + await rootBundle.load("assets/audios/select.mp3"), + ); - removeSoundId ??= await _soundpool! - .load(await rootBundle.load("assets/audios/remove.mp3")); - if (removeSoundId == null) { - if (Config.developerMode) { - assert(false); - } - debugPrint("[audio] Error: removeSoundId is null."); - return; - } + _winSoundId = await _soundpool.load( + await rootBundle.load("assets/audios/win.mp3"), + ); - selectSoundId ??= await _soundpool! - .load(await rootBundle.load("assets/audios/select.mp3")); - if (selectSoundId == null) { - if (Config.developerMode) { - assert(false); - } - debugPrint("[audio] Error: selectSoundId is null."); - return; - } - - winSoundId ??= - await _soundpool!.load(await rootBundle.load("assets/audios/win.mp3")); - if (winSoundId == null) { - if (Config.developerMode) { - assert(false); - } - debugPrint("[audio] Error: winSoundId is null."); - return; - } + _initialized = true; } - static Future _playSound(int? soundId) async { - if (Platform.isWindows) { - return; + static Future _playSound(Sound sound) async { + assert(!Platform.isWindows); + + late final int soundId; + + switch (sound) { + case Sound.draw: + soundId = _drawSoundId; + break; + case Sound.fly: + soundId = _flySoundId; + break; + case Sound.go: + soundId = _goSoundId; + break; + case Sound.illegal: + soundId = _illegalSoundId; + break; + case Sound.lose: + soundId = _loseSoundId; + break; + case Sound.mill: + soundId = _millSoundId; + break; + case Sound.place: + soundId = _placeSoundId; + break; + case Sound.remove: + soundId = _removeSoundId; + break; + case Sound.select: + soundId = _selectSoundId; + break; + case Sound.win: + soundId = _winSoundId; + break; } - if (soundId == null) { - if (Config.developerMode) { - assert(false); - } - debugPrint("[audio] Error: soundId is null."); - return; - } - - _alarmSoundStreamId = await _soundpool!.play(soundId); + _alarmSoundStreamId = await _soundpool.play(soundId); } static Future _stopSound() async { - if (Platform.isWindows) { - return; - } + assert(!Platform.isWindows); - if (_alarmSoundStreamId != null && _alarmSoundStreamId! > 0) { - await _soundpool!.stop(_alarmSoundStreamId!); + if (_alarmSoundStreamId > 0) { + await _soundpool.stop(_alarmSoundStreamId); } } - static Future disposePool() async { - if (Platform.isWindows) { - return; - } + static void disposePool() { + assert(!Platform.isWindows); - _soundpool!.dispose(); + _soundpool.dispose(); } - static Future playTone(int? soundId) async { - Chain.capture(() async { + static Future playTone(Sound sound) async { + await Chain.capture(() async { if (!Config.toneEnabled || isTemporaryMute || - Config.screenReaderSupport) { + Config.screenReaderSupport || + !_initialized) { return; } @@ -206,14 +173,11 @@ class Audios { return; } + // TODO: isn't debug chain meant to catch errors? so why catching them in here and not in onError?? try { - if (_soundpool == null) { - await loadSounds(); - } - await _stopSound(); - _playSound(soundId); + await _playSound(sound); } catch (e) { // Fallback for all errors debugPrint(e.toString()); diff --git a/src/ui/flutter_app/lib/shared/drawer_controller.dart b/src/ui/flutter_app/lib/shared/drawer_controller.dart index 94a14dfe..2ebde3e7 100644 --- a/src/ui/flutter_app/lib/shared/drawer_controller.dart +++ b/src/ui/flutter_app/lib/shared/drawer_controller.dart @@ -111,7 +111,7 @@ class _DrawerControllerState extends State super.initState(); } - Future getInitState() async { + bool getInitState() { scrollController.jumpTo( widget.drawerWidth, );